-
Notifications
You must be signed in to change notification settings - Fork 598
feat: zk sumcheck #7517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: zk sumcheck #7517
Changes from all commits
36523c1
4692e4c
da478a6
ffdf418
573f13c
be77077
ec0bfc7
0fbf781
051a966
afa284e
74cf2e7
2487f75
63bd81b
d449355
c5fab2b
4dbae08
eaeca2d
14fb4e1
c2bf3f6
0c07e19
2002fe3
1dd93cc
94c95f3
e75c4f9
70d04c9
332eab9
f6916b9
c62a745
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -443,6 +443,8 @@ template <typename T> concept IsFoldingFlavor = IsAnyOf<T, UltraFlavor, | |
| UltraRecursiveFlavor_<CircuitSimulatorBN254>, | ||
| MegaRecursiveFlavor_<UltraCircuitBuilder>, | ||
| MegaRecursiveFlavor_<MegaCircuitBuilder>, MegaRecursiveFlavor_<CircuitSimulatorBN254>>; | ||
| template <typename T> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do you need this concept to be so complicated? |
||
| concept FlavorHasZK = T::HasZK; | ||
|
|
||
| template <typename Container, typename Element> | ||
| inline std::string flavor_get_label(Container&& container, const Element& element) { | ||
|
|
||
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| #pragma once | ||
|
|
||
| #include "barretenberg/flavor/flavor.hpp" | ||
| #include <array> | ||
| #include <optional> | ||
| #include <vector> | ||
|
|
@@ -11,15 +11,34 @@ namespace bb { | |
| * =(u_0,\ldots, u_{d-1})\f$. These are computed by \ref bb::SumcheckProver< Flavor > "Sumcheck Prover" and need to be | ||
| * checked using Zeromorph. | ||
| */ | ||
| template <typename Flavor> struct SumcheckOutput { | ||
| template <typename Flavor, typename = void> struct SumcheckOutput { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this typename = void needed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so that claimed_libra_evaluations are added to the output automatically if the flavor has zk, this way I don't need to deel with the optional access |
||
| using FF = typename Flavor::FF; | ||
| using ClaimedEvaluations = typename Flavor::AllValues; | ||
| // \f$ \vec u = (u_0, ..., u_{d-1}) \f$ | ||
| std::vector<FF> challenge; | ||
| // Evaluations in \f$ \vec u \f$ of the polynomials used in Sumcheck | ||
| // Evaluations at \f$ \vec u \f$ of the polynomials used in Sumcheck | ||
| ClaimedEvaluations claimed_evaluations; | ||
| // Whether or not the evaluations of multilinear polynomials \f$ P_1, \ldots, P_N \f$ and final Sumcheck evaluation | ||
| // have been confirmed | ||
| std::optional<bool> verified = false; // optional b/c this struct is shared by the Prover/Verifier | ||
| }; | ||
| /** | ||
| * @brief A modification of SumcheckOutput required by ZK Flavors where a vector of evaluations of Libra univariates is | ||
| * included. | ||
| * | ||
| * @tparam Flavor | ||
| */ | ||
| template <typename Flavor> struct SumcheckOutput<Flavor, std::enable_if_t<FlavorHasZK<Flavor>>> { | ||
| using FF = typename Flavor::FF; | ||
| using ClaimedEvaluations = typename Flavor::AllValues; | ||
| // \f$ \vec u = (u_0, ..., u_{d-1}) \f$ | ||
| std::vector<FF> challenge; | ||
| // Evaluations at \f$ \vec u \f$ of the polynomials used in Sumcheck | ||
| ClaimedEvaluations claimed_evaluations; | ||
| // Include ClaimedLibraEvaluations conditioned on FlavorHasZK concept | ||
| std::vector<FF> claimed_libra_evaluations; | ||
| // Whether or not the evaluations of multilinear polynomials \f$ P_1, \ldots, P_N \f$ and final Sumcheck evaluation | ||
| // have been confirmed | ||
| std::optional<bool> verified = false; // Optional b/c this struct is shared by the Prover/Verifier | ||
| }; | ||
| } // namespace bb | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| #include "barretenberg/relations/relation_types.hpp" | ||
| #include "barretenberg/relations/utils.hpp" | ||
| #include "barretenberg/stdlib/primitives/bool/bool.hpp" | ||
| #include "zk_sumcheck_data.hpp" | ||
|
|
||
| namespace bb { | ||
|
|
||
|
|
@@ -59,9 +60,8 @@ template <typename Flavor> class SumcheckProverRound { | |
| * "MAX_PARTIAL_RELATION_LENGTH + 1". | ||
| */ | ||
| static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; | ||
|
|
||
| using SumcheckRoundUnivariate = bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>; | ||
| SumcheckTupleOfTuplesOfUnivariates univariate_accumulators; | ||
|
|
||
| // Prover constructor | ||
| SumcheckProverRound(size_t initial_round_size) | ||
| : round_size(initial_round_size) | ||
|
|
@@ -87,7 +87,9 @@ template <typename Flavor> class SumcheckProverRound { | |
| input in the first round, or from the \ref multivariates table. Using general method | ||
| \ref bb::Univariate::extend_to "extend_to", the evaluations of these polynomials are extended from the | ||
| domain \f$ \{0,1\} \f$ to the domain \f$ \{0,\ldots, D\} \f$ required for the computation of the round univariate. | ||
|
|
||
| * In the case when witness polynomials are masked (ZK Flavors), this method has to distinguish between witness and | ||
| * non-witness polynomials. The witness univariates obtained from witness multilinears are corrected by a masking | ||
| * quadratic term extended to the same length MAX_PARTIAL_RELATION_LENGTH. | ||
| * Should only be called externally with relation_idx equal to 0. | ||
| * In practice, #multivariates is either ProverPolynomials or PartiallyEvaluatedMultivariates. | ||
| * | ||
|
|
@@ -98,13 +100,33 @@ template <typename Flavor> class SumcheckProverRound { | |
| */ | ||
| template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates> | ||
| void extend_edges(ExtendedEdges& extended_edges, | ||
| const ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates, | ||
| size_t edge_idx) | ||
| ProverPolynomialsOrPartiallyEvaluatedMultivariates& multivariates, | ||
| size_t edge_idx, | ||
| std::optional<ZKSumcheckData<Flavor>> zk_sumcheck_data = std::nullopt) | ||
| { | ||
| for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) { | ||
| bb::Univariate<FF, 2> edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); | ||
| extended_edge = edge.template extend_to<MAX_PARTIAL_RELATION_LENGTH>(); | ||
| } | ||
|
|
||
| if constexpr (!Flavor::HasZK) { | ||
| for (auto [extended_edge, multivariate] : zip_view(extended_edges.get_all(), multivariates.get_all())) { | ||
| bb::Univariate<FF, 2> edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); | ||
| extended_edge = edge.template extend_to<MAX_PARTIAL_RELATION_LENGTH>(); | ||
| } | ||
| } else { | ||
| // extend edges of witness polynomials and add correcting terms | ||
| for (auto [extended_edge, multivariate, masking_univariate] : | ||
| zip_view(extended_edges.get_all_witnesses(), | ||
| multivariates.get_all_witnesses(), | ||
| zk_sumcheck_data.value().masking_terms_evaluations)) { | ||
| bb::Univariate<FF, 2> edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); | ||
| extended_edge = edge.template extend_to<MAX_PARTIAL_RELATION_LENGTH>(); | ||
| extended_edge += masking_univariate; | ||
| }; | ||
| // extend edges of public polynomials | ||
| for (auto [extended_edge, multivariate] : | ||
| zip_view(extended_edges.get_non_witnesses(), multivariates.get_non_witnesses())) { | ||
| bb::Univariate<FF, 2> edge({ multivariate[edge_idx], multivariate[edge_idx + 1] }); | ||
| extended_edge = edge.template extend_to<MAX_PARTIAL_RELATION_LENGTH>(); | ||
| }; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -130,11 +152,13 @@ template <typename Flavor> class SumcheckProverRound { | |
| method \ref extend_and_batch_univariates "extend and batch univariates". | ||
| */ | ||
| template <typename ProverPolynomialsOrPartiallyEvaluatedMultivariates> | ||
| bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH> compute_univariate( | ||
| SumcheckRoundUnivariate compute_univariate( | ||
| const size_t round_idx, | ||
| ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials, | ||
| const bb::RelationParameters<FF>& relation_parameters, | ||
| const bb::PowPolynomial<FF>& pow_polynomial, | ||
| const RelationSeparator alpha) | ||
| const RelationSeparator alpha, | ||
| std::optional<ZKSumcheckData<Flavor>> zk_sumcheck_data = std::nullopt) // only submitted when Flavor HasZK | ||
| { | ||
| BB_OP_COUNT_TIME(); | ||
|
|
||
|
|
@@ -162,8 +186,11 @@ template <typename Flavor> class SumcheckProverRound { | |
| size_t end = (thread_idx + 1) * iterations_per_thread; | ||
|
|
||
| for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) { | ||
| extend_edges(extended_edges[thread_idx], polynomials, edge_idx); | ||
|
|
||
| if constexpr (!Flavor::HasZK) { | ||
| extend_edges(extended_edges[thread_idx], polynomials, edge_idx); | ||
| } else { | ||
| extend_edges(extended_edges[thread_idx], polynomials, edge_idx, zk_sumcheck_data); | ||
| } | ||
| // Compute the \f$ \ell \f$-th edge's univariate contribution, | ||
| // scale it by the corresponding \f$ pow_{\beta} \f$ contribution and add it to the accumulators for \f$ | ||
| // \tilde{S}^i(X_i) \f$. If \f$ \ell \f$'s binary representation is given by \f$ (\ell_{i+1},\ldots, | ||
|
|
@@ -180,10 +207,19 @@ template <typename Flavor> class SumcheckProverRound { | |
| for (auto& accumulators : thread_univariate_accumulators) { | ||
| Utils::add_nested_tuples(univariate_accumulators, accumulators); | ||
| } | ||
|
|
||
| // For ZK Flavors: The evaluations of the round univariates are masked by the evaluations of Libra univariates | ||
| if constexpr (Flavor::HasZK) { | ||
| auto libra_round_univariate = compute_libra_round_univariate(zk_sumcheck_data.value(), round_idx); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be good to get yourself used to not overusing |
||
| // Batch the univariate contributions from each sub-relation to obtain the round univariate | ||
| auto round_univariate = | ||
| batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alpha, pow_polynomial); | ||
| // Mask the round univariate | ||
| return round_univariate + libra_round_univariate; | ||
| } | ||
| // Batch the univariate contributions from each sub-relation to obtain the round univariate | ||
| return batch_over_relations<bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>>( | ||
| univariate_accumulators, alpha, pow_polynomial); | ||
| else { | ||
| return batch_over_relations<SumcheckRoundUnivariate>(univariate_accumulators, alpha, pow_polynomial); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -263,6 +299,32 @@ template <typename Flavor> class SumcheckProverRound { | |
| Utils::apply_to_tuple_of_tuples(tuple, extend_and_sum); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Compute Libra round univariate expressed given by the formula | ||
| \f{align}{ | ||
| \texttt{libra_round_univariate}_i(k) = | ||
| \rho \cdot 2^{d-1-i} \left(\sum_{j = 0}^{i-1} g_j(u_{j}) + g_{i,k}+ | ||
| \sum_{j=i+1}^{d-1}\left(g_{j,0}+g_{j,1}\right)\right) | ||
| = \texttt{libra_univariates}_{i}(k) + \texttt{libra_running_sum} | ||
| \f}. | ||
| * | ||
| * @param zk_sumcheck_data | ||
| * @param round_idx | ||
| */ | ||
| static SumcheckRoundUnivariate compute_libra_round_univariate(ZKSumcheckData<Flavor> zk_sumcheck_data, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rather than carrying the round_idx you could determine it based on round length
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do you think it would be more readable? cause sumcheck prover round is operating on the size of polynomials/2^{round_idx}. i thought about deriving it this way, but seems more complicated |
||
| size_t round_idx) | ||
| { | ||
| SumcheckRoundUnivariate libra_round_univariate; | ||
| // select the i'th column of Libra book-keeping table | ||
| auto current_column = zk_sumcheck_data.libra_univariates[round_idx]; | ||
| // the evaluation of Libra round univariate at k=0...D are equal to \f$\texttt{libra_univariates}_{i}(k)\f$ | ||
| // corrected by the Libra running sum | ||
| for (size_t idx = 0; idx < BATCHED_RELATION_PARTIAL_LENGTH; ++idx) { | ||
| libra_round_univariate.value_at(idx) = current_column.value_at(idx) + zk_sumcheck_data.libra_running_sum; | ||
| }; | ||
| return libra_round_univariate; | ||
| } | ||
|
|
||
| private: | ||
| /** | ||
| * @brief In Round \f$ i \f$, for a given point \f$ \vec \ell \in \{0,1\}^{d-1 - i}\f$, calculate the contribution | ||
|
|
@@ -295,7 +357,6 @@ template <typename Flavor> class SumcheckProverRound { | |
| const FF& scaling_factor) | ||
| { | ||
| using Relation = std::tuple_element_t<relation_idx, Relations>; | ||
|
|
||
| // Check if the relation is skippable to speed up accumulation | ||
| if constexpr (!isSkippable<Relation, decltype(extended_edges)>) { | ||
| // If not, accumulate normally | ||
|
|
@@ -310,7 +371,6 @@ template <typename Flavor> class SumcheckProverRound { | |
| scaling_factor); | ||
| } | ||
| } | ||
|
|
||
| // Repeat for the next relation. | ||
| if constexpr (relation_idx + 1 < NUM_RELATIONS) { | ||
| accumulate_relation_univariates<relation_idx + 1>( | ||
|
|
@@ -340,6 +400,7 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| public: | ||
| using FF = typename Flavor::FF; | ||
| using ClaimedEvaluations = typename Flavor::AllValues; | ||
| using ClaimedLibraEvaluations = typename std::vector<FF>; | ||
|
|
||
| bool round_failed = false; | ||
| /** | ||
|
|
@@ -352,6 +413,7 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| * MAX_PARTIAL_RELATION_LENGTH "MAX_PARTIAL_RELATION_LENGTH + 1". | ||
| */ | ||
| static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; | ||
| using SumcheckRoundUnivariate = bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>; | ||
|
|
||
| FF target_total_sum = 0; | ||
|
|
||
|
|
@@ -370,7 +432,7 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| * @param univariate Round univariate \f$\tilde{S}^{i}\f$ represented by its evaluations over \f$0,\ldots,D\f$. | ||
| * | ||
| */ | ||
| bool check_sum(bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>& univariate) | ||
| bool check_sum(SumcheckRoundUnivariate& univariate) | ||
| { | ||
| FF total_sum = univariate.value_at(0) + univariate.value_at(1); | ||
| // TODO(#673): Conditionals like this can go away once native verification is is just recursive verification | ||
|
|
@@ -437,7 +499,7 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| * @param round_challenge \f$ u_i\f$ | ||
| * @return FF \f$ \sigma_{i+1} = \tilde{S}^i(u_i)\f$ | ||
| */ | ||
| FF compute_next_target_sum(bb::Univariate<FF, BATCHED_RELATION_PARTIAL_LENGTH>& univariate, FF& round_challenge) | ||
| FF compute_next_target_sum(SumcheckRoundUnivariate& univariate, FF& round_challenge) | ||
| { | ||
| // Evaluate \f$\tilde{S}^{i}(u_{i}) \f$ | ||
| target_total_sum = univariate.evaluate(round_challenge); | ||
|
|
@@ -473,7 +535,8 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| FF compute_full_honk_relation_purported_value(ClaimedEvaluations purported_evaluations, | ||
| const bb::RelationParameters<FF>& relation_parameters, | ||
| const bb::PowPolynomial<FF>& pow_polynomial, | ||
| const RelationSeparator alpha) | ||
| const RelationSeparator alpha, | ||
| std::optional<FF> full_libra_purported_value = std::nullopt) | ||
| { | ||
| // The verifier should never skip computation of contributions from any relation | ||
| Utils::template accumulate_relation_evaluations_without_skipping<>( | ||
|
|
@@ -482,6 +545,9 @@ template <typename Flavor> class SumcheckVerifierRound { | |
| FF running_challenge{ 1 }; | ||
| FF output{ 0 }; | ||
| Utils::scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output); | ||
| if constexpr (Flavor::HasZK) { | ||
| output += full_libra_purported_value.value(); | ||
| }; | ||
| return output; | ||
| } | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #pragma once | ||
|
|
||
| #include <array> | ||
| #include <optional> | ||
| #include <vector> | ||
|
|
||
| namespace bb { | ||
|
|
||
| /** | ||
| * @brief This structure is created to contain various polynomials and constants required by ZK Sumcheck. | ||
| * | ||
| */ | ||
| template <typename Flavor> struct ZKSumcheckData { | ||
| using FF = typename Flavor::FF; | ||
| /** | ||
| * @brief The total algebraic degree of the Sumcheck relation \f$ F \f$ as a polynomial in Prover Polynomials | ||
| * \f$P_1,\ldots, P_N\f$. | ||
| */ | ||
| static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::MAX_PARTIAL_RELATION_LENGTH; | ||
| // The number of all witnesses including shifts and derived witnesses from flavors that have ZK, | ||
| // otherwise, set this constant to 0. | ||
| /** | ||
| * @brief The total algebraic degree of the Sumcheck relation \f$ F \f$ as a polynomial in Prover Polynomials | ||
| * \f$P_1,\ldots, P_N\f$ <b> incremented by </b> 1, i.e. it is equal \ref MAX_PARTIAL_RELATION_LENGTH | ||
| * "MAX_PARTIAL_RELATION_LENGTH + 1". | ||
| */ | ||
| static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; | ||
| // Initialize the length of the array of evaluation masking scalars as 0 for non-ZK Flavors and as | ||
| // NUM_ALL_WITNESS_ENTITIES for ZK FLavors | ||
| static constexpr size_t MASKING_SCALARS_LENGTH = Flavor::HasZK ? Flavor::NUM_ALL_WITNESS_ENTITIES : 0; | ||
| // Array of random scalars used to hide the witness info from leaking through the claimed evaluations | ||
| using EvalMaskingScalars = std::array<FF, MASKING_SCALARS_LENGTH>; | ||
| // Auxiliary table that represents the evaluations of quadratic polynomials r_j * X(1-X) at 0,..., | ||
| // MAX_PARTIAL_RELATION_LENGTH - 1 | ||
| using EvaluationMaskingTable = std::array<bb::Univariate<FF, MAX_PARTIAL_RELATION_LENGTH>, MASKING_SCALARS_LENGTH>; | ||
| // The size of the LibraUnivariates. We ensure that they do not take extra space when Flavor runs non-ZK | ||
| // Sumcheck. | ||
| static constexpr size_t LIBRA_UNIVARIATES_LENGTH = Flavor::HasZK ? Flavor::BATCHED_RELATION_PARTIAL_LENGTH : 0; | ||
| // Container for the Libra Univariates. Their number depends on the size of the circuit. | ||
| using LibraUnivariates = std::vector<bb::Univariate<FF, LIBRA_UNIVARIATES_LENGTH>>; | ||
| // Container for the evaluations of Libra Univariates that have to be proven. | ||
| using ClaimedLibraEvaluations = std::vector<FF>; | ||
|
|
||
| EvalMaskingScalars eval_masking_scalars; | ||
| EvaluationMaskingTable masking_terms_evaluations; | ||
| LibraUnivariates libra_univariates; | ||
| FF libra_scaling_factor{ 1 }; | ||
| FF libra_challenge; | ||
| FF libra_running_sum; | ||
| ClaimedLibraEvaluations libra_evaluations; | ||
| }; | ||
|
|
||
| } // namespace bb |
Uh oh!
There was an error while loading. Please reload this page.