From c6785f9af0ad582debca9140a9b3ecef68c87b76 Mon Sep 17 00:00:00 2001 From: maramihali Date: Fri, 2 Feb 2024 17:23:51 +0000 Subject: [PATCH] feat: folding `GoblinUltra` instances in ProtoGalaxy (#4340) Adds the missing functionality to be able to fold `GoblinUltra` instances to PG prover and verifier (both native and recursive). On one hand, this includes committing to the additional witness polynomials ( ECC op wires and data bus related polynomials) as well as computing and committing to the log derivativee inverses similar to the pre-sumcheck rounds in the UltraProver We also need to add extra functionality to be able to fold linearly dependent relations. Such relations (in our codebase, just one subrelation part of the data bus relations) operate on the entire execution trace rather than a single row. They don't have to hold at each row in part which implies that we won't multiply them by the pow polynomial. This requires us to make modifications just to the perturbator (F polynomial). The combiner (`G` polynomial) makes use of the `accumulate` function, which is implemented for every subrelation we have which, in the case of linearly dependent relations, does not use the `scaling_factor` provided as argument. In the perturbator, we accumulate the evaluation of the full honk relation at each row and return a vector of `FF`. We modify it to add to the value at index 0 (representing row 0) also the `linear_dependent_contribution` (the value of the linear dependent subrelation over the entire execution trace) _batched_ by its coresponding batching challenge `alpha` (recall we have a batching challenge for each subrelation). Otherwise than that the protocol remains unchanged. Closes https://github.com/AztecProtocol/barretenberg/issues/753. --- cpp/src/barretenberg/flavor/goblin_ultra.hpp | 64 +- .../flavor/goblin_ultra_recursive.hpp | 3 +- .../barretenberg/flavor/ultra_recursive.hpp | 3 +- .../protogalaxy/protogalaxy_prover.cpp | 32 +- .../protogalaxy/protogalaxy_prover.hpp | 37 +- .../protogalaxy/protogalaxy_verifier.cpp | 23 + cpp/src/barretenberg/relations/utils.hpp | 74 +- .../protogalaxy_recursive_verifier.cpp | 22 + .../protogalaxy_recursive_verifier.test.cpp | 470 +++++++------ .../ultra_honk/protogalaxy.test.cpp | 649 ++++++++++-------- 10 files changed, 848 insertions(+), 529 deletions(-) diff --git a/cpp/src/barretenberg/flavor/goblin_ultra.hpp b/cpp/src/barretenberg/flavor/goblin_ultra.hpp index deee8860d..5ff14f552 100644 --- a/cpp/src/barretenberg/flavor/goblin_ultra.hpp +++ b/cpp/src/barretenberg/flavor/goblin_ultra.hpp @@ -380,36 +380,35 @@ class GoblinUltraFlavor { calldata_read_counts = "CALLDATA_READ_COUNTS"; lookup_inverses = "LOOKUP_INVERSES"; - // The ones beginning with "__" are only used for debugging - q_c = "__Q_C"; - q_l = "__Q_L"; - q_r = "__Q_R"; - q_o = "__Q_O"; - q_4 = "__Q_4"; - q_m = "__Q_M"; - q_arith = "__Q_ARITH"; - q_sort = "__Q_SORT"; - q_elliptic = "__Q_ELLIPTIC"; - q_aux = "__Q_AUX"; - q_lookup = "__Q_LOOKUP"; - q_busread = "__Q_BUSREAD"; - q_poseidon2_external = "__Q_POSEIDON2_EXTERNAL"; - q_poseidon2_internal = "__Q_POSEIDON2_INTERNAL"; - sigma_1 = "__SIGMA_1"; - sigma_2 = "__SIGMA_2"; - sigma_3 = "__SIGMA_3"; - sigma_4 = "__SIGMA_4"; - id_1 = "__ID_1"; - id_2 = "__ID_2"; - id_3 = "__ID_3"; - id_4 = "__ID_4"; - table_1 = "__TABLE_1"; - table_2 = "__TABLE_2"; - table_3 = "__TABLE_3"; - table_4 = "__TABLE_4"; - lagrange_first = "__LAGRANGE_FIRST"; - lagrange_last = "__LAGRANGE_LAST"; - lagrange_ecc_op = "__Q_ECC_OP_QUEUE"; + q_c = "Q_C"; + q_l = "Q_L"; + q_r = "Q_R"; + q_o = "Q_O"; + q_4 = "Q_4"; + q_m = "Q_M"; + q_arith = "Q_ARITH"; + q_sort = "Q_SORT"; + q_elliptic = "Q_ELLIPTIC"; + q_aux = "Q_AUX"; + q_lookup = "Q_LOOKUP"; + q_busread = "Q_BUSREAD"; + q_poseidon2_external = "Q_POSEIDON2_EXTERNAL"; + q_poseidon2_internal = "Q_POSEIDON2_INTERNAL"; + sigma_1 = "SIGMA_1"; + sigma_2 = "SIGMA_2"; + sigma_3 = "SIGMA_3"; + sigma_4 = "SIGMA_4"; + id_1 = "ID_1"; + id_2 = "ID_2"; + id_3 = "ID_3"; + id_4 = "ID_4"; + table_1 = "TABLE_1"; + table_2 = "TABLE_2"; + table_3 = "TABLE_3"; + table_4 = "TABLE_4"; + lagrange_first = "LAGRANGE_FIRST"; + lagrange_last = "LAGRANGE_LAST"; + lagrange_ecc_op = "Q_ECC_OP_QUEUE"; }; }; @@ -458,15 +457,16 @@ class GoblinUltraFlavor { this->w_l = commitments.w_l; this->w_r = commitments.w_r; this->w_o = commitments.w_o; - this->sorted_accum = commitments.sorted_accum; this->w_4 = commitments.w_4; + this->sorted_accum = commitments.sorted_accum; this->z_perm = commitments.z_perm; this->z_lookup = commitments.z_lookup; this->ecc_op_wire_1 = commitments.ecc_op_wire_1; this->ecc_op_wire_2 = commitments.ecc_op_wire_2; this->ecc_op_wire_3 = commitments.ecc_op_wire_3; + this->ecc_op_wire_4 = commitments.ecc_op_wire_4; this->calldata = commitments.calldata; - this->calldata = commitments.calldata_read_counts; + this->calldata_read_counts = commitments.calldata_read_counts; this->lookup_inverses = commitments.lookup_inverses; } } diff --git a/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index 559b3a9a2..16cfefff8 100644 --- a/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -46,7 +46,8 @@ template class GoblinUltraRecursiveFlavor_ { using FF = typename Curve::ScalarField; using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; - using NativeVerificationKey = GoblinUltraFlavor::VerificationKey; + using NativeFlavor = GoblinUltraFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = bb::VerifierCommitmentKey; diff --git a/cpp/src/barretenberg/flavor/ultra_recursive.hpp b/cpp/src/barretenberg/flavor/ultra_recursive.hpp index 9666b1e86..0c8aede2d 100644 --- a/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -54,7 +54,8 @@ template class UltraRecursiveFlavor_ { using Commitment = typename Curve::Element; using CommitmentHandle = typename Curve::Element; using FF = typename Curve::ScalarField; - using NativeVerificationKey = UltraFlavor::VerificationKey; + using NativeFlavor = UltraFlavor; + using NativeVerificationKey = NativeFlavor::VerificationKey; // Note(luke): Eventually this may not be needed at all using VerifierCommitmentKey = bb::VerifierCommitmentKey; diff --git a/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index f5273d654..c0efde0a3 100644 --- a/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/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); @@ -303,7 +334,6 @@ FoldingResult ProtoGalaxyProver_proof_data; res.accumulator = next_accumulator; - return res; } diff --git a/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index 46d4305a3..026c83a61 100644 --- a/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/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,17 +157,22 @@ 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( + 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 - * factor of X. + * @brief Recursively compute the parent nodes of each level in the tree, 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 factor of X. */ static std::vector construct_coefficients_tree(const std::vector& betas, const std::vector& deltas, @@ -307,7 +319,8 @@ template class ProtoGalaxyProver_ { FF pow_challenge = pow_betas[idx]; // Accumulate the i-th row's univariate contribution. Note that the relation parameters passed to this - // function have already been folded + // function have already been folded. Moreover, linear-dependent relations that act over the entire + // execution trace rather than on rows, will not be multiplied by the pow challenge. 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; @@ -416,7 +430,8 @@ template class ProtoGalaxyProver_ { } /** - * @brief Compute the next accumulator (ϕ*, ω*\vec{\beta*}, e*), send the public data ϕ* and the folding parameters + * @brief Compute the next accumulator (ϕ*, ω*, \vec{\beta*}, e*), send the public data ϕ* and the folding + * parameters * (\vec{\beta*}, e*) to the verifier and return the complete accumulator * * @details At this stage, we assume that the instances have the same size and the same number of public parameter.s diff --git a/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 02923df59..bee65a680 100644 --- a/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/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 and databus 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 constexpr (IsGoblinFlavor) { + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial + 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/cpp/src/barretenberg/relations/utils.hpp b/cpp/src/barretenberg/relations/utils.hpp index 331967359..9818249f3 100644 --- a/cpp/src/barretenberg/relations/utils.hpp +++ b/cpp/src/barretenberg/relations/utils.hpp @@ -184,7 +184,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 then sum them * @param challenges Array of NUM_SUBRELATIONS - 1 challenges (because the first subrelation does not need to be * scaled) * @param result Batched result @@ -208,7 +208,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(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 +282,33 @@ template class RelationUtils { apply_to_tuple_of_arrays(operation, tuple); } } + + /** + * @brief Recursive template function to apply a specific operation on each element of several arrays in a tuple + * + * @details We need this method in addition to the apply_to_tuple_of_arrays when we aim to perform different + * operations depending on the array element. More explicitly, in our codebase this method is used when the elements + * of array are values of subrelations and we want to accumulate some of these values separately (the linearly + * dependent contribution when we compute the evaluation of full rel_U(G)H at particular row.) + */ + template + static void apply_to_tuple_of_arrays_elements(Operation&& operation, std::tuple& tuple) + { + using Relation = typename std::tuple_element_t; + const auto subrelation_length = 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 < subrelation_length) { + // 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/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp b/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp index 48f6d655c..e0fed9ac6 100644 --- a/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp +++ b/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/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp b/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp index 34cfdfb30..0912edf91 100644 --- a/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp +++ b/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp @@ -8,31 +8,33 @@ #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 UltraComposer = ::bb::UltraComposer_; using GoblinUltraComposer = ::bb::UltraComposer_; - using InnerFlavor = UltraFlavor; - using InnerComposer = UltraComposer; + using InnerFlavor = typename RecursiveFlavor::NativeFlavor; + using InnerComposer = ::bb::UltraComposer_; using Instance = ::bb::ProverInstance_; using InnerBuilder = typename InnerComposer::CircuitBuilder; using InnerCurve = bn254; - using Commitment = InnerFlavor::Commitment; - using FF = InnerFlavor::FF; + using Commitment = typename InnerFlavor::Commitment; + using FF = typename InnerFlavor::FF; - // Types for recursive verifier circuit - // cannot do on Goblin + // Types for veryfing a recursive verifier circuit using OuterBuilder = GoblinUltraCircuitBuilder; - using RecursiveFlavor = ::bb::UltraRecursiveFlavor_; - using RecursiveVerifierInstances = VerifierInstances_; + using OuterComposer = GoblinUltraComposer; + + using RecursiveVerifierInstances = ::bb::VerifierInstances_; using FoldingRecursiveVerifier = ProtoGalaxyRecursiveVerifier_; using DeciderRecursiveVerifier = DeciderRecursiveVerifier_; using DeciderVerifier = DeciderVerifier_; using NativeVerifierInstances = VerifierInstances_; using NativeFoldingVerifier = 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() { @@ -55,12 +57,13 @@ class ProtogalaxyRecursiveTest : public testing::Test { */ static void create_inner_circuit(InnerBuilder& builder, size_t log_num_gates = 10) { - using fr_ct = InnerCurve::ScalarField; - using fq_ct = InnerCurve::BaseField; - using public_witness_ct = InnerCurve::public_witness_ct; - using witness_ct = InnerCurve::witness_ct; - using byte_array_ct = InnerCurve::byte_array_ct; + using fr_ct = typename InnerCurve::ScalarField; + using fq_ct = typename InnerCurve::BaseField; + using public_witness_ct = typename InnerCurve::public_witness_ct; + using witness_ct = typename InnerCurve::witness_ct; + using byte_array_ct = typename 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; @@ -99,14 +102,87 @@ 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; + + 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(); + } }; - public: - static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + static std::shared_ptr fold_and_verify_native(const std::vector>& instances, + InnerComposer& composer) + { + 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, true); + return next_accumulator; + } + + /** + *@brief Create inner circuit and call check_circuit on it + */ + static void test_inner_circuit() + { + InnerBuilder builder; + + create_inner_circuit(builder); + + bool result = builder.check_circuit(); + EXPECT_EQ(result, true); + }; + + /** + * @brief Ensure that evaluating the perturbator in the recursive folding verifier returns the same result as + * evaluating in Polynomial class. + * + */ + 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); - static std::shared_ptr fold_and_verify(const std::vector>& instances, - InnerComposer& inner_composer) + auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); + EXPECT_EQ(res1, res2.get_value()); + }; + + /** + * @brief Tests a simple recursive fold that is valid works as expected. + * + */ + 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 }; + // Generate a folding proof auto inner_folding_prover = inner_composer.create_folding_prover(instances); auto inner_folding_proof = inner_folding_prover.fold_instances(); @@ -115,16 +191,16 @@ 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 auto native_folding_verifier = inner_composer.create_folding_verifier(); auto native_folding_result = native_folding_verifier.verify_folding_proof(inner_folding_proof.folding_data); - EXPECT_EQ(native_folding_result, outer_folding_circuit.check_circuit()); + EXPECT_EQ(native_folding_result, !outer_folding_circuit.failed()); - // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring - // the manifests produced by each agree. + // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring the + // manifestsproduced by each agree. auto recursive_folding_manifest = verifier.transcript->get_manifest(); auto native_folding_manifest = native_folding_verifier.transcript->get_manifest(); @@ -135,214 +211,196 @@ class ProtogalaxyRecursiveTest : public testing::Test { // Check for a failure flag in the recursive verifier circuit EXPECT_EQ(outer_folding_circuit.failed(), false) << outer_folding_circuit.err(); - return inner_folding_proof.accumulator; - } -}; -/** - * @brief Create inner circuit and call check_circuit on it - * - */ -TEST_F(ProtogalaxyRecursiveTest, InnerCircuit) -{ - InnerBuilder builder; + { + auto composer = OuterComposer(); + auto instance = composer.create_instance(outer_folding_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); - create_inner_circuit(builder); + ASSERT(verified); + } + }; - bool result = builder.check_circuit(); - EXPECT_EQ(result, true); -} + /** + * @brief Perform two rounds of folding valid circuits and then recursive verify the final decider proof, + * make sure the verifer circuits pass check_circuit(). Ensure that the algorithm of the recursive and native + * verifiers are identical by checking the manifests + */ + // TODO(https://github.com/AztecProtocol/barretenberg/issues/844): Fold the recursive folding verifier in tests once + // we can fold instances of different sizes. + 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_native(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_native(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]); + } -/** - * @brief Ensure that evaluating the perturbator in the recursive folding verifier returns the same result as - * 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); + // 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); - auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); - EXPECT_EQ(res1, res2.get_value()); -} + ASSERT(verified); + } + }; -/** - * @brief Tests a simple recursive fold that is valid works as expected. - * - */ -TEST_F(ProtogalaxyRecursiveTest, RecursiveFoldingTest) -{ - // Create two arbitrary circuits for the first round of folding - InnerBuilder builder1; + 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); + 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 }; + 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); -} + auto accumulator = fold_and_verify_native(instances, inner_composer); -/** - * @brief Recursively verify two rounds of folding valid circuits and then recursive verify the final decider proof, - * make sure the verifer circuits pass check_circuit(). Ensure that the algorithm of the recursive and native verifiers - * are identical by checking the manifests + // Tamper with the accumulator by changing the target sum + accumulator->target_sum = FF::random_element(); - */ -TEST_F(ProtogalaxyRecursiveTest, FullProtogalaxyRecursiveTest) -{ + // 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 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]); - } + // 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()); + }; - // Construct and verify a proof of the recursive decider verifier circuit + static void test_tampered_accumulator() { - 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); - } -} + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; -TEST_F(ProtogalaxyRecursiveTest, 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_native(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(); - create_inner_circuit(builder1); - InnerBuilder builder2; - builder2.add_public_variable(FF(1)); - create_inner_circuit(builder2); + // 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); + }; +}; - InnerComposer inner_composer = InnerComposer(); - auto instance1 = inner_composer.create_instance(builder1); - auto instance2 = inner_composer.create_instance(builder2); - auto instances = std::vector>{ instance1, instance2 }; +using FlavorTypes = testing::Types, + UltraRecursiveFlavor_>; +TYPED_TEST_SUITE(ProtoGalaxyRecursiveTests, FlavorTypes); - auto accumulator = fold_and_verify(instances, inner_composer); +TYPED_TEST(ProtoGalaxyRecursiveTests, InnerCircuit) +{ + TestFixture::test_inner_circuit(); +} + +TYPED_TEST(ProtoGalaxyRecursiveTests, NewEvaluate) +{ + TestFixture::test_new_evaluate(); +} - // Tamper with the accumulator by changing the target sum - accumulator->target_sum = FF::random_element(); +TYPED_TEST(ProtoGalaxyRecursiveTests, RecursiveFoldingTest) +{ + TestFixture::test_recursive_folding(); +} - // 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(); +TYPED_TEST(ProtoGalaxyRecursiveTests, FullProtogalaxyRecursiveTest) +{ - // 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); + TestFixture::test_full_protogalaxy_recursive(); +} - // We expect the decider circuit check to fail due to the bad proof - EXPECT_FALSE(outer_decider_circuit.check_circuit()); +TYPED_TEST(ProtoGalaxyRecursiveTests, TamperedDeciderProof) +{ + 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/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp b/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp index 651c05fa1..f0237d439 100644 --- a/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp +++ b/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/polynomials/pow.hpp" #include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/ultra_honk/ultra_composer.hpp" @@ -6,340 +7,438 @@ using namespace bb; namespace { -using Flavor = UltraFlavor; -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; 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; -} - -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; -} - -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]; - } - - 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); -} -class ProtoGalaxyTests : public ::testing::Test { +template class ProtoGalaxyTests : public testing::Test { public: + using Composer = UltraComposer_; + using VerificationKey = typename Flavor::VerificationKey; + using Instance = ProverInstance_; + using Instances = ProverInstances_; + using ProtoGalaxyProver = ProtoGalaxyProver_; + using FF = typename Flavor::FF; + using Affine = typename Flavor::Commitment; + using Projective = typename Flavor::GroupElement; + using Builder = typename Flavor::CircuitBuilder; + using Polynomial = typename Flavor::Polynomial; + using ProverPolynomials = typename Flavor::ProverPolynomials; + using RelationParameters = bb::RelationParameters; + using WitnessCommitments = typename Flavor::WitnessCommitments; + using CommitmentKey = typename Flavor::CommitmentKey; + using PowPolynomial = bb::PowPolynomial; + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } -}; -} // namespace -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 void construct_circuit(Builder& builder) + { + if constexpr (IsGoblinFlavor) { + GoblinMockCircuits::construct_arithmetic_circuit(builder); + GoblinMockCircuits::construct_goblin_ecc_op_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) }); + } } - 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 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, 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]); - } -} -TEST_F(ProtoGalaxyTests, PerturbatorPolynomial) -{ - using RelationSeparator = Flavor::RelationSeparator; - const size_t log_instance_size(3); - const size_t instance_size(1 << log_instance_size); - - std::array, NUM_POLYNOMIALS> random_polynomials; - for (auto& poly : random_polynomials) { - poly = get_random_polynomial(instance_size); + 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_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 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]; + } + EXPECT_EQ(accumulator->target_sum == expected_target_sum, expected_result); } - 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 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 decider_proof = decider_prover.construct_proof(); + auto verified = decider_verifier.verify_proof(decider_proof); + EXPECT_EQ(verified, expected_result); } - // Construct pow(\vec{betas}) as in the paper - auto pow_beta = bb::PowPolynomial(betas); - pow_beta.compute_values(); + /** + * @brief For a valid circuit, ensures that computing the value of the full UH/UGH relation at each row in its + * execution trace (with the contribution of the linearly dependent one added tot he first row, in case of Goblin) + * will be 0. + * + */ + static void test_full_honk_evaluations_valid_circuit() + { + 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)); + } + } - // 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]; + /** + * @brief Check the coefficients of the perturbator computed from dummy \vec{β}, \vec{δ} and f_i(ω) will be the same + * as if computed manually. + * + */ + 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 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; + /** + * @brief Create a dummy accumulator and ensure coefficient 0 of the computed perturbator is the same as the + * accumulator's target sum. + * + */ + static void test_pertubator_polynomial() + { + using RelationSeparator = typename 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 = bb::Polynomial::random(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 deltas = ProtoGalaxyProver::compute_round_challenge_pows(log_instance_size, FF::random_element()); - auto perturbator = ProtoGalaxyProver::compute_perturbator(accumulator, deltas); + /** + * @brief Manually compute the expected evaluations of the combiner quotient, given evaluations of the combiner and + * check them against the evaluations returned by the function. + * + */ + 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)); + } + } - // Ensure the constant coefficient of the perturbator is equal to the target sum as indicated by the paper - EXPECT_EQ(perturbator[0], target_sum); -} + /** + * @brief For two dummy instances with their relation parameter η set, check that combining them in a univariate, + * barycentrially extended to the desired number of evaluations, is performed correctly. + * + */ + 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; + + 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); + } -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)); + /** + * @brief Given two dummy instances with the batching challenges alphas set (one for each subrelation) ensure + * combining them in a univariate of desired length works as expected. + */ + static void test_combine_alpha() + { + using Instances = ProverInstances_; + using Instance = typename Instances::Instance; + + Builder builder1; + auto instance1 = std::make_shared(builder1); + instance1->alphas.fill(2); + + Builder builder2; + builder2.add_variable(3); + auto instance2 = std::make_shared(builder2); + instance2->alphas.fill(4); + + 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; + /** + * @brief Testing two valid rounds of folding followed by the decider. + * + */ + 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); + /** + * @brief Ensure tampering a commitment and then calling the decider causes the decider verification to fail. + * + */ + 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); -} + /** + * @brief Ensure tampering an accumulator and then calling fold again causes both the folding verification and + * decider verification to fail. + * + */ + 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); + } +}; +} // namespace - 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::test_full_honk_evaluations_valid_circuit(); +} - 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); +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