diff --git a/barretenberg/cpp/src/barretenberg/constants.hpp b/barretenberg/cpp/src/barretenberg/constants.hpp index 364600b2ef8d..218761ae5506 100644 --- a/barretenberg/cpp/src/barretenberg/constants.hpp +++ b/barretenberg/cpp/src/barretenberg/constants.hpp @@ -16,4 +16,8 @@ static constexpr uint32_t CONST_ECCVM_LOG_N = 15; static constexpr uint32_t MAX_LOOKUP_TABLES_SIZE = 75000; static constexpr uint32_t MAX_DATABUS_SIZE = 10000; + +// The number of entries in ProverPolynomials reserved for randomness intended to mask witness commitments, witness +// evaluation at the sumcheck challenge, and, if necessary, the evaluation of the corresponding shift +static constexpr uint32_t MASKING_OFFSET = 4; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp index 69f4f1799e6f..6fcea1cb32aa 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_circuit_builder.hpp @@ -4,6 +4,7 @@ #include "./msm_builder.hpp" #include "./precomputed_tables_builder.hpp" #include "./transcript_builder.hpp" +#include "barretenberg/constants.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/honk/proof_system/logderivative_library.hpp" @@ -218,7 +219,7 @@ class ECCVMCircuitBuilder { [[nodiscard]] size_t get_circuit_subgroup_size(const size_t num_rows) const { - const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows)); + const auto num_rows_log2 = static_cast(numeric::get_msb64(num_rows + MASKING_OFFSET)); size_t num_rows_pow2 = 1UL << (num_rows_log2 + (1UL << num_rows_log2 == num_rows ? 0 : 1)); return num_rows_pow2; } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index a44e827d90e8..040157daf01d 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -84,8 +84,10 @@ class ECCVMFlavor { // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation - // length = 3 - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + // length = 3. + // The degree has to be further increased by 1 because the relation is multiplied by the Row Disabling // + // Polynomial + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 2; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate @@ -515,7 +517,8 @@ class ECCVMFlavor { const auto [msm_rows, point_table_read_counts] = ECCVMMSMMBuilder::compute_rows( msms, builder.get_number_of_muls(), builder.op_queue->get_num_msm_rows()); - const size_t num_rows = std::max({ point_table_rows.size(), msm_rows.size(), transcript_rows.size() }); + const size_t num_rows = + std::max({ point_table_rows.size(), msm_rows.size(), transcript_rows.size() }) + MASKING_OFFSET; const auto log_num_rows = static_cast(numeric::get_msb64(num_rows)); const size_t dyadic_num_rows = 1UL << (log_num_rows + (1UL << log_num_rows == num_rows ? 0 : 1)); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index d95a2f01ad76..4a4cfe0a6d23 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -5,7 +5,6 @@ #include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" #include "barretenberg/common/ref_array.hpp" #include "barretenberg/honk/proof_system/logderivative_library.hpp" -#include "barretenberg/honk/proof_system/permutation_library.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp index 5565d5197ee9..9328260c87e8 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp @@ -55,7 +55,7 @@ void construct_lookup_read_counts(typename Flavor::Polynomial& read_counts, { const size_t tables_size = circuit.get_tables_size(); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1033): construct tables and counts at top of trace - size_t table_offset = dyadic_circuit_size - tables_size; + size_t table_offset = dyadic_circuit_size - tables_size - MASKING_OFFSET; // loop over all tables used in the circuit; each table contains data about the lookups made on it for (auto& table : circuit.lookup_tables) { diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp index 0cd69e9f523d..84b04a5e331d 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp @@ -55,7 +55,7 @@ TEST_F(ComposerLibTests, LookupReadCounts) // The table polys are constructed at the bottom of the trace, thus so to are the counts/tags // TODO(https://github.com/AztecProtocol/barretenberg/issues/1033): construct tables and counts at top of trace - size_t offset = circuit_size - builder.get_tables_size(); + size_t offset = circuit_size - builder.get_tables_size() - MASKING_OFFSET; // The uint32 XOR lookup table is constructed for 6 bit operands via double for loop that iterates through the left // operand externally (0 to 63) then the right operand internally (0 to 63). Computing (1 XOR 5) will thus result in diff --git a/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp b/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp new file mode 100644 index 000000000000..e5c2abe7f67e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/polynomials/row_disabling_polynomial.hpp @@ -0,0 +1,179 @@ +#pragma once +#include "barretenberg/common/compiler_hints.hpp" +#include "barretenberg/common/op_count.hpp" +#include "barretenberg/common/thread.hpp" +#include "barretenberg/stdlib/primitives/bool/bool.hpp" + +#include +#include +namespace bb { +/** + * @struct RowDisablingPolynomial + * @brief Polynomial for Sumcheck with disabled Rows + * + * \f$ n = 2^d \f$ circuit size + * \f$ L_i \f$ multilinear Lagrange in \f$ d \f$ variables, \f$ i = 0,\ldots, n-1 \f$. + * + * Assume we are given a "valid" execution trace at rows \f$ 0,\ldots, n-5 \f$, i.e., + * \f[ + * \sum_{\mathbb{H} \setminus \{n-1, n-2, n-3, n-4\}} H = 0. + * \f] + * + * We want to pad the witness polynomials with random field elements in rows \f$ n-1, n-2, n-3 \f$. + * Since the commitment to the shift must coincide with the commitment to its unshifted counterpart, + * we have to reserve \f$ 4 \f$ rows at the end to be able to. + * To achieve this, we multiply the Honk relation \f$ H \f$ by the polynomial + * \f[ + * 1 - L = 1 - L_{n-1} - L_{n-2} - L_{n-3} - L_{n-4}. + * \f] + * that vanishes at the last \f$ 4 \f$ rows and is equal to \f$ 1 \f$ everywhere else on the hypercube. + * + * We consider the sumcheck protocol for the modified relation + * \f[ + * \sum_{\mathbb{H}} (1 - L) H = \sum_{\mathbb{H}} H - \sum_{\mathbb{H}} L \cdot H. + * \f] + * + * Note that the target sum remains \f$ 0 \f$ because the contributions from the last rows are multiplied by \f$ 0 \f$. + * + * Recall: + * - \f$ n-1 = 2^d - 1 = (1,1, \ldots, 1) \f$ + * - \f$ n-2 = (0,1,\ldots,1) \f$ + * - \f$ n-3 = (1,0,\ldots,1) \f$ + * - \f$ n-4 = (0,0,\ldots,1) \f$ + * + * ### Round 0: + * \f[ + * \begin{aligned} + * S' &= + * S_{H,0} - \Big(L_{n-1}(X, 1, \ldots, 1) + L_{n-2}(X, 1,\ldots,1)\Big) H(X,1,\ldots, 1) \\ + * &\quad - \Big(L_{n-3}(X, 0,1,\ldots,1) + L_{n-4}(X,0,1,\ldots,1)\Big) H(X,0,1,\ldots,1) + * \end{aligned} + * \f] + * + * We do not modify the algorithm computing \f$ S_{H,0} \f$. Simply add a method that computes the contribution from the + * edges \f$ (0,1,\ldots,1) \f$ and \f$ (1,\ldots,1) \in \mathbb{H}^{d-1} \f$. + * + * First, compute the coefficients in the Lagrange basis of the factor coming from the Lagranges: + * \f[ + * \begin{aligned} + * L_{n-1}(X,\vec{1}) + L_{n-2}(X,\vec{1}) &= X + (1 - X) = 1 \\ + * L_{n-3}(X,0,1,\ldots,1) + L_{n-4}(X,0,1,\ldots,1) &= 1 + * \end{aligned} + * \f] + * + * \f[ + * S'_0 = S_{H,0} - H(X,1,\ldots,1) - H(X,0,1,\ldots,1) + * \f] + * + * ### Round 1: + * \f[ + * \begin{aligned} + * L_{n-1}(u_0,X,\vec{1}) + L_{n-2}(u_0,X,\vec{1}) &= + * u_0 X + (1 - u_0) X = X \\ + * L_{n-3}(u_0,X,\vec{1}) + L_{n-4}(u_0,X,\vec{1}) &= + * u_0 (1 - X) + (1 - u_0)(1 - X) = (1 - X) + * \end{aligned} + * \f] + * + * \f[ + * S'_1 = S_{H,1} - H(X,1,\ldots,1) + * \f] + * + * ### Round 2: + * \f[ + * S'_2 = S_{H,2} - X \cdot H(u_0,u_1,X,1,\ldots,1) + * \f] + * + * ### Rounds i > 1: + * We can compute the restricted sumcheck univariates \f$ S' \f$ in each round as follows: + * \f[ + * \begin{aligned} + * S' = S_{H,i} - + * \Big(L_{n-1}(u_0, \ldots, u_{i-1}, X, 1, \ldots, 1) + L_{n-2}(u_0, \ldots, u_{i-1}, X, 1,\ldots,1) \\ + * + L_{n-3}(u_0, \ldots, u_{i-1}, X, 1,\ldots,1) + L_{n-4}(u_0, \ldots, u_{i-1}, X, 1,\ldots,1)\Big) \\ + * \times H(u_0, \ldots, u_{i-1}, X, 1,\ldots,1) + * \end{aligned} + * \f] + * + * Compute the factor coming from the Lagranges: + * \f[ + * \begin{aligned} + * &\left(u_0 \cdots u_{i-1} + (1 - u_0) u_1 \cdots u_{i-1} + u_0 (1 - u_1) u_2 \cdots u_{i-1} + (1 - u_0)(1 - u_1) u_2 + * \cdots u_{i-1}\right) X \\ + * &= \left(u_0 u_1 + (1 - u_0) u_1 + u_0 (1 - u_1) + (1 - u_0)(1 - u_1)\right) u_2 \cdots u_{i-1} X \\ + * &= 0 \cdot (1 - X) + 1 \cdot u_2 \cdots u_{i-1} \cdot X + * \end{aligned} + * \f] + * + * This way, we get: + * \f[ + * S_{H,i}(X) = S_{H,i} - u_2 \cdots u_{i-1} \cdot X \cdot H(u_0, \ldots, u_{i-1}, X, 1, \ldots, 1). + * \f] + * + * ## The algorithm: + * + * Let \f$ D \f$ be the max partial degree of \f$ H \f$. + * + * 1. Compute \f$ S_{H,i} \f$ without any modifications as a polynomial of degree \f$ D \f$. Extend it to degree \f$ D + + * 1 \f$, because it is the max partial degree of \f$ L \cdot H \f$. + * + * 2. If \f$ i = 0 \f$, compute \f$ H(X,1,1,\ldots,1) + H(X,0,1,\ldots,1) \f$ as a univariate of degree \f$ D \f$, else + * compute \f$ H(u_0, \ldots, u_{i-1}, X, 1, \ldots, 1) \f$ as a univariate of degree \f$ D \f$. Extend to degree \f$ D + * + 1 \f$. + * + * 3. Compute the extension of \f$ L^{(i)} = L(u_0, \ldots, u_{i-1}, X, 1, \ldots, 1) \f$ to the degree \f$ D + 1 \f$ + * polynomial. + * + * 4. Compute the coefficients of the product \f$ L^{(i)} \cdot H^{(i)} \f$. + * + * 5. Compute the coefficients of \f$ S_{H,i} - L^{(i)} \cdot H^{(i)} \f$ (degree \f$ D + 1 \f$ univariate). + * + * The verifier needs to evaluate \f$ 1 - L(u_0, \ldots, u_{d-1}) \f$, which is equal to \f$ 0 \f$ if \f$ d < 2 \f$, and + * is equal to \f$ 1- u_2 \cdots u_{d-1} \f$ otherwise. + */ + +template struct RowDisablingPolynomial { + // initialized as a constant linear polynomial = 1 + FF eval_at_0{ 1 }; + FF eval_at_1{ 1 }; + + RowDisablingPolynomial() = default; + /** + * @brief Compute the evaluations of L^{(i)} at 0 and 1. + * + * @details In every round, the contribution from the Honk relation computed at + * disabled rows has to be mutiplied by \f$ L^{(i)} \f$, which is a linear combination of Lagrange polynomials + * defined above. + * + * @param round_challenge Sumcheck round challenge + * @param round_idx Sumcheck round index + */ + void update_evaluations(FF round_challenge, size_t round_idx) + { + if (round_idx == 1) { + eval_at_0 = FF{ 0 }; + } + if (round_idx >= 2) { + eval_at_1 *= round_challenge; + } + } + /** + * @brief Compute the evaluation of \f$ 1 - L \f$ at the sumcheck challenge + * + * @param multivariate_challenge + * @param log_circuit_size + * @return FF + */ + static FF evaluate_at_challenge(std::vector multivariate_challenge, const size_t log_circuit_size) + { + FF evaluation_at_multivariate_challenge{ 1 }; + + for (size_t idx = 2; idx < log_circuit_size; idx++) { + evaluation_at_multivariate_challenge *= multivariate_challenge[idx]; + } + + return FF{ 1 } - evaluation_at_multivariate_challenge; + } +}; + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp index 637fe115c07c..c634e9a281df 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/eccvm_verifier/eccvm_recursive_flavor.hpp @@ -54,12 +54,12 @@ template class ECCVMRecursiveFlavor_ { // think these two are not needed for recursive verifier land // using GrandProductRelations = std::tuple>; // using LookupRelation = ECCVMLookupRelation; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = ECCVMFlavor::MAX_PARTIAL_RELATION_LENGTH; // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation // length = 3 - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = ECCVMFlavor::BATCHED_RELATION_PARTIAL_LENGTH; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // Instantiate the BarycentricData needed to extend each Relation Univariate diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp index ffbf91dea89d..e83001c1e909 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_flavor.hpp @@ -97,7 +97,7 @@ template class TranslatorRecursiveFlavor_ { // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation // length = 3 - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = NativeFlavor::BATCHED_RELATION_PARTIAL_LENGTH; static constexpr size_t NUM_RELATIONS = std::tuple_size_v; // define the containers for storing the contributions from each relation in Sumcheck diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp index 59e3776201d3..dbaac3d0164a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp @@ -12,6 +12,8 @@ class MegaZKFlavor : public bb::MegaFlavor { public: // Indicates that this flavor runs with non-ZK Sumcheck. static constexpr bool HasZK = true; + // The degree has to be increased because the relation is multiplied by the Row Disabling Polynomial + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MegaFlavor::BATCHED_RELATION_PARTIAL_LENGTH + 1; /** * @brief Derived class that defines proof structure for Mega proofs, as well as supporting functions. * Note: Made generic for use in MegaRecursive. diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp index f7cc6ba65002..c6ddab34dbe7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp @@ -20,17 +20,11 @@ class UltraFlavorWithZK : public bb::UltraFlavor { public: // This flavor runs with ZK Sumcheck static constexpr bool HasZK = true; - // Compute the maximum over all partial subrelation lengths incremented by the corresponding subrelation witness - // degrees for the Relations inherited from UltraFlavor - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_total_relation_length(); // Determine the number of evaluations of Prover and Libra Polynomials that the Prover sends to the Verifier in // the rounds of ZK Sumcheck. - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = UltraFlavor::BATCHED_RELATION_PARTIAL_LENGTH + 1; // Construct the container for the subrelations' contributions - using SumcheckTupleOfTuplesOfUnivariates = - decltype(create_sumcheck_tuple_of_tuples_of_univariates()); - // Re-define ExtendedEdges to account for the incremented MAX_PARTIAL_RELATION_LENGTH - using ExtendedEdges = ProverUnivariates; + using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates()); }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp index 887730da4727..6bb555ad49e5 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp @@ -197,11 +197,17 @@ template class SumcheckProver { std::vector multivariate_challenge; multivariate_challenge.reserve(multivariate_d); size_t round_idx = 0; + RowDisablingPolynomial row_disabling_polynomial; // In the first round, we compute the first univariate polynomial and populate the book-keeping table of // #partially_evaluated_polynomials, which has \f$ n/2 \f$ rows and \f$ N \f$ columns. When the Flavor has ZK, // compute_univariate also takes into account the zk_sumcheck_data. - auto round_univariate = round.compute_univariate( - round_idx, full_polynomials, relation_parameters, gate_separators, alpha, zk_sumcheck_data); + auto round_univariate = round.compute_univariate(round_idx, + full_polynomials, + relation_parameters, + gate_separators, + alpha, + zk_sumcheck_data, + row_disabling_polynomial); vinfo("starting sumcheck rounds..."); { @@ -216,6 +222,7 @@ template class SumcheckProver { // Prepare ZK Sumcheck data for the next round if constexpr (Flavor::HasZK) { update_zk_sumcheck_data(zk_sumcheck_data, round_challenge, round_idx); + row_disabling_polynomial.update_evaluations(round_challenge, round_idx); }; gate_separators.partially_evaluate(round_challenge); round.round_size = round.round_size >> 1; // TODO(#224)(Cody): Maybe partially_evaluate should do this and @@ -232,7 +239,8 @@ template class SumcheckProver { relation_parameters, gate_separators, alpha, - zk_sumcheck_data); + zk_sumcheck_data, + row_disabling_polynomial); // Place evaluations of Sumcheck Round Univariate in the transcript transcript->send_to_verifier("Sumcheck:univariate_" + std::to_string(round_idx), round_univariate); FF round_challenge = transcript->template get_challenge("Sumcheck:u_" + std::to_string(round_idx)); @@ -242,6 +250,7 @@ template class SumcheckProver { // Prepare evaluation masking and libra structures for the next round (for ZK Flavors) if constexpr (Flavor::HasZK) { update_zk_sumcheck_data(zk_sumcheck_data, round_challenge, round_idx); + row_disabling_polynomial.update_evaluations(round_challenge, round_idx); }; gate_separators.partially_evaluate(round_challenge); @@ -590,10 +599,21 @@ template class SumcheckVerifier { for (auto [eval, transcript_eval] : zip_view(purported_evaluations.get_all(), transcript_evaluations)) { eval = transcript_eval; } + // For ZK Flavors: the evaluation of the Row Disabling Polynomial at the sumcheck challenge + FF correcting_factor{ 1 }; + if constexpr (Flavor::HasZK) { + RowDisablingPolynomial row_disabler = RowDisablingPolynomial(); + correcting_factor = row_disabler.evaluate_at_challenge(multivariate_challenge, multivariate_d); + } + // Evaluate the Honk relation at the point (u_0, ..., u_{d-1}) using claimed evaluations of prover polynomials. // In ZK Flavors, the evaluation is corrected by full_libra_purported_value - FF full_honk_purported_value = round.compute_full_relation_purported_value( - purported_evaluations, relation_parameters, gate_separators, alpha, full_libra_purported_value); + FF full_honk_purported_value = round.compute_full_relation_purported_value(purported_evaluations, + relation_parameters, + gate_separators, + alpha, + full_libra_purported_value, + correcting_factor); bool final_check(false); //! [Final Verification Step] if constexpr (IsRecursiveFlavor) { diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp index 88cdfd405e6f..ffbc979548e0 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp @@ -172,7 +172,7 @@ template class SumcheckTests : public ::testing::Test { // TODO(#225): make the inputs to this test more interesting, e.g. non-trivial permutations void test_prover_verifier_flow() { - const size_t multivariate_d(2); + const size_t multivariate_d(3); const size_t multivariate_n(1 << multivariate_d); // Construct prover polynomials where each is the zero polynomial. @@ -186,8 +186,7 @@ template class SumcheckTests : public ::testing::Test { // Add some non-trivial values to certain polynomials so that the arithmetic relation will have non-trivial // contribution. Note: since all other polynomials are set to 0, all other relations are trivially // satisfied. - std::array w_l; - w_l = { 0, 1, 2, 0 }; + std::array w_l = { 0, 1, 2, 0 }; std::array w_r = { 0, 1, 2, 0 }; std::array w_o = { 0, 2, 4, 0 }; std::array w_4 = { 0, 0, 0, 0 }; @@ -199,6 +198,29 @@ template class SumcheckTests : public ::testing::Test { std::array q_arith = { 0, 1, 1, 0 }; // Setting all of these to 0 ensures the GrandProductRelation is satisfied + // For ZK Flavors: add some randomness to ProverPolynomials + if constexpr (Flavor::HasZK) { + w_l[7] = FF::random_element(); + w_r[6] = FF::random_element(); + w_4[6] = FF::random_element(); + auto z_1 = FF::random_element(); + auto z_2 = FF::random_element(); + auto r = FF::random_element(); + + std::array z_perm = { 0, 0, 0, 0, 0, 0, z_1, z_2 }; + std::array lookup_inverses = { 0, 0, 0, 0, 0, 0, r * r, r }; + + full_polynomials.z_perm = bb::Polynomial(z_perm); + full_polynomials.lookup_inverses = bb::Polynomial(lookup_inverses); + + if constexpr (std::is_same::value) { + std::array ecc_op_wire = { 0, 0, 0, 0, 0, 0, r * r * r, w_4[6] }; + std::array return_data_inverses = { 0, 0, 0, 0, 0, 0, FF(7) * r * r, -r }; + + full_polynomials.ecc_op_wire_1 = bb::Polynomial(ecc_op_wire); + full_polynomials.return_data_inverses = bb::Polynomial(return_data_inverses); + } + } full_polynomials.w_l = bb::Polynomial(w_l); full_polynomials.w_r = bb::Polynomial(w_r); full_polynomials.w_o = bb::Polynomial(w_o); @@ -259,7 +281,9 @@ template class SumcheckTests : public ::testing::Test { void test_failure_prover_verifier_flow() { - const size_t multivariate_d(2); + // Since the last 4 rows in ZK Flavors are disabled, we extend an invalid circuit of size 4 to size 8 by padding + // with 0. + const size_t multivariate_d(3); const size_t multivariate_n(1 << multivariate_d); // Construct prover polynomials where each is the zero polynomial. diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index 758797b8bcc4..95515bf0d631 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -2,6 +2,7 @@ #include "barretenberg/common/thread.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/gate_separator.hpp" +#include "barretenberg/polynomials/row_disabling_polynomial.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/relations/relation_types.hpp" #include "barretenberg/relations/utils.hpp" @@ -141,7 +142,8 @@ template class SumcheckProverRound { const bb::RelationParameters& relation_parameters, const bb::GateSeparatorPolynomial& gate_sparators, const RelationSeparator alpha, - ZKSumcheckData zk_sumcheck_data) // only populated when Flavor HasZK + ZKSumcheckData zk_sumcheck_data, // only populated when Flavor HasZK + RowDisablingPolynomial row_disabling_poly) { PROFILE_THIS_NAME("compute_univariate"); @@ -187,13 +189,16 @@ template class SumcheckProverRound { Utils::add_nested_tuples(univariate_accumulators, accumulators); } // For ZK Flavors: The evaluations of the round univariates are masked by the evaluations of Libra univariates + // and corrected by subtracting the contribution from the disabled rows if constexpr (Flavor::HasZK) { + const auto contribution_from_disabled_rows = compute_disabled_contribution( + polynomials, relation_parameters, gate_sparators, alpha, round_idx, row_disabling_poly); const auto libra_round_univariate = compute_libra_round_univariate(zk_sumcheck_data, round_idx); // Batch the univariate contributions from each sub-relation to obtain the round univariate const auto round_univariate = batch_over_relations(univariate_accumulators, alpha, gate_sparators); // Mask the round univariate - return round_univariate + libra_round_univariate; + return round_univariate + libra_round_univariate - contribution_from_disabled_rows; } // Batch the univariate contributions from each sub-relation to obtain the round univariate else { @@ -201,6 +206,45 @@ template class SumcheckProverRound { } } + /*! + * @brief For ZK Flavors: A method disabling the last 4 rows of the ProverPolynomials + * + * @details See description of RowDisablingPolynomial + * + */ + template + SumcheckRoundUnivariate compute_disabled_contribution( + ProverPolynomialsOrPartiallyEvaluatedMultivariates& polynomials, + const bb::RelationParameters& relation_parameters, + const bb::GateSeparatorPolynomial& gate_sparators, + const RelationSeparator alpha, + const size_t round_idx, + const RowDisablingPolynomial row_disabling_polynomial) + { + SumcheckTupleOfTuplesOfUnivariates univariate_accumulator; + ExtendedEdges extended_edges; + SumcheckRoundUnivariate result; + + // In Round 0, we have to compute the contribution from 2 edges: n - 1 = (1,1,...,1) and n-4 = (0,1,...,1). + size_t start_edge_idx = (round_idx == 0) ? round_size - 4 : round_size - 2; + + for (size_t edge_idx = start_edge_idx; edge_idx < round_size; edge_idx += 2) { + extend_edges(extended_edges, polynomials, edge_idx); + accumulate_relation_univariates(univariate_accumulator, + extended_edges, + relation_parameters, + gate_sparators[(edge_idx >> 1) * gate_sparators.periodicity]); + } + result = batch_over_relations(univariate_accumulator, alpha, gate_sparators); + bb::Univariate row_disabling_factor = + bb::Univariate({ row_disabling_polynomial.eval_at_0, row_disabling_polynomial.eval_at_1 }); + SumcheckRoundUnivariate row_disabling_factor_extended = + row_disabling_factor.template extend_to(); + result *= row_disabling_factor_extended; + + return result; + } + /** * @brief Given a tuple of tuples of extended per-relation contributions, \f$ (t_0, t_1, \ldots, * t_{\text{NUM_SUBRELATIONS}-1}) \f$ and a challenge \f$ \alpha \f$, scale them by the relation separator @@ -497,7 +541,8 @@ template class SumcheckVerifierRound { const bb::RelationParameters& relation_parameters, const bb::GateSeparatorPolynomial& gate_sparators, const RelationSeparator alpha, - std::optional full_libra_purported_value = std::nullopt) + const FF full_libra_purported_value = FF{ 0 }, + FF correcting_factor = FF{ 1 }) { // The verifier should never skip computation of contributions from any relation Utils::template accumulate_relation_evaluations_without_skipping<>( @@ -507,7 +552,7 @@ template class SumcheckVerifierRound { FF output{ 0 }; Utils::scale_and_batch_elements(relation_evaluations, alpha, running_challenge, output); if constexpr (Flavor::HasZK) { - output += full_libra_purported_value.value(); + output = output * correcting_factor + full_libra_purported_value; if constexpr (IsECCVMRecursiveFlavor) { output.self_reduce(); } diff --git a/barretenberg/cpp/src/barretenberg/trace_to_polynomials/trace_to_polynomials.cpp b/barretenberg/cpp/src/barretenberg/trace_to_polynomials/trace_to_polynomials.cpp index 39eccd6ef01a..f9d3405d2f8f 100644 --- a/barretenberg/cpp/src/barretenberg/trace_to_polynomials/trace_to_polynomials.cpp +++ b/barretenberg/cpp/src/barretenberg/trace_to_polynomials/trace_to_polynomials.cpp @@ -2,9 +2,9 @@ #include "barretenberg/flavor/plonk_flavors.hpp" #include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" #include "barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp" namespace bb { template void TraceToPolynomials::populate_public_inputs_block(Builder& builder) @@ -175,6 +175,7 @@ void TraceToPolynomials::add_ecc_op_wires_to_proving_key(Builder& builde } template class TraceToPolynomials; +template class TraceToPolynomials; template class TraceToPolynomials; template class TraceToPolynomials; template class TraceToPolynomials; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp index 7167437d4e3c..0221f95fedfa 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_flavor.hpp @@ -123,8 +123,9 @@ class TranslatorFlavor { // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation - // length = 3 - static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + // length = 3. + // The degree has to be further increased because the relation is multiplied by the Row Disabling Polynomial + static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 2; static constexpr size_t NUM_RELATIONS = std::tuple_size_v; // define the containers for storing the contributions from each relation in Sumcheck diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp index 01fad4e3974d..f1efed42252a 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/databus.test.cpp @@ -11,31 +11,34 @@ #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" +using namespace bb; namespace { -auto& engine = bb::numeric::get_debug_randomness(); -} +auto& engine = numeric::get_debug_randomness(); + +using FlavorTypes = ::testing::Types; -namespace bb { -class DataBusTests : public ::testing::Test { +template class DataBusTests : public ::testing::Test { protected: static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } using Curve = curve::BN254; using FF = Curve::ScalarField; - using Builder = MegaCircuitBuilder; + using Builder = typename Flavor::CircuitBuilder; + using Prover = UltraProver_; + using Verifier = UltraVerifier_; // Construct and verify a MegaHonk proof for a given circuit static bool construct_and_verify_proof(MegaCircuitBuilder& builder) { - MegaProver prover{ builder }; - auto verification_key = std::make_shared(prover.proving_key->proving_key); - MegaVerifier verifier{ verification_key }; + Prover prover{ builder }; + auto verification_key = std::make_shared(prover.proving_key->proving_key); + Verifier verifier{ verification_key }; auto proof = prover.construct_proof(); return verifier.verify_proof(proof); } // Construct a Mega circuit with some arbitrary sample gates - static MegaCircuitBuilder construct_test_builder() + static Builder construct_test_builder() { auto op_queue = std::make_shared(); auto builder = MegaCircuitBuilder{ op_queue }; @@ -111,54 +114,56 @@ class DataBusTests : public ::testing::Test { } }; +TYPED_TEST_SUITE(DataBusTests, FlavorTypes); + /** * @brief Test proof construction/verification for a circuit with calldata lookup gates * */ -TEST_F(DataBusTests, CallDataRead) +TYPED_TEST(DataBusTests, CallDataRead) { - Builder builder = construct_test_builder(); - construct_circuit_with_calldata_reads(builder); + typename TypeParam::CircuitBuilder builder = this->construct_test_builder(); + this->construct_circuit_with_calldata_reads(builder); - EXPECT_TRUE(construct_and_verify_proof(builder)); + EXPECT_TRUE(this->construct_and_verify_proof(builder)); } /** * @brief Test proof construction/verification for a circuit with secondary_calldata lookup gates * */ -TEST_F(DataBusTests, CallData2Read) +TYPED_TEST(DataBusTests, CallData2Read) { - Builder builder = construct_test_builder(); - construct_circuit_with_secondary_calldata_reads(builder); + typename TypeParam::CircuitBuilder builder = this->construct_test_builder(); + this->construct_circuit_with_secondary_calldata_reads(builder); - EXPECT_TRUE(construct_and_verify_proof(builder)); + EXPECT_TRUE(this->construct_and_verify_proof(builder)); } /** * @brief Test proof construction/verification for a circuit with return data lookup gates * */ -TEST_F(DataBusTests, ReturnDataRead) +TYPED_TEST(DataBusTests, ReturnDataRead) { - Builder builder = construct_test_builder(); - construct_circuit_with_return_data_reads(builder); + typename TypeParam::CircuitBuilder builder = this->construct_test_builder(); + this->construct_circuit_with_return_data_reads(builder); - EXPECT_TRUE(construct_and_verify_proof(builder)); + EXPECT_TRUE(this->construct_and_verify_proof(builder)); } /** * @brief Test proof construction/verification for a circuit with reads from all bus columns * */ -TEST_F(DataBusTests, ReadAll) +TYPED_TEST(DataBusTests, ReadAll) { - Builder builder = construct_test_builder(); - construct_circuit_with_calldata_reads(builder); - construct_circuit_with_secondary_calldata_reads(builder); - construct_circuit_with_return_data_reads(builder); + typename TypeParam::CircuitBuilder builder = this->construct_test_builder(); + this->construct_circuit_with_calldata_reads(builder); + this->construct_circuit_with_secondary_calldata_reads(builder); + this->construct_circuit_with_return_data_reads(builder); - EXPECT_TRUE(construct_and_verify_proof(builder)); + EXPECT_TRUE(this->construct_and_verify_proof(builder)); } /** @@ -166,12 +171,14 @@ TEST_F(DataBusTests, ReadAll) * the read results are correct * */ -TEST_F(DataBusTests, CallDataDuplicateRead) +TYPED_TEST(DataBusTests, CallDataDuplicateRead) { // Construct a circuit and add some ecc op gates and arithmetic gates - auto builder = construct_test_builder(); + typename TypeParam::CircuitBuilder builder = this->construct_test_builder(); + using FF = TypeParam::FF; // Add some values to calldata + std::vector calldata_values = { 7, 10, 3, 12, 1 }; for (auto& val : calldata_values) { builder.add_public_calldata(builder.add_variable(val)); @@ -201,8 +208,7 @@ TEST_F(DataBusTests, CallDataDuplicateRead) EXPECT_EQ(duplicate_read_result_2, expected_read_result_at_1); // Construct and verify Honk proof - bool result = construct_and_verify_proof(builder); + bool result = this->construct_and_verify_proof(builder); EXPECT_TRUE(result); } - -} // namespace bb \ No newline at end of file +} // namespace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.cpp index 246c62632000..712452585ed5 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.cpp @@ -24,7 +24,8 @@ template size_t DeciderProvingKey_::compute_dyadi // The number of gates is the maximum required by the lookup argument or everything else, plus an optional zero row // to allow for shifts. - size_t total_num_gates = num_zero_rows + std::max(min_size_due_to_lookups, min_size_of_execution_trace); + size_t total_num_gates = + MASKING_OFFSET + num_zero_rows + std::max(min_size_due_to_lookups, min_size_of_execution_trace); // Next power of 2 (dyadic circuit size) return circuit.get_circuit_subgroup_size(total_num_gates); @@ -97,8 +98,9 @@ void DeciderProvingKey_::allocate_table_lookup_polynomials(const Circuit { PROFILE_THIS_NAME("allocate_table_lookup_polynomials"); - const size_t max_tables_size = std::min(static_cast(MAX_LOOKUP_TABLES_SIZE), dyadic_circuit_size - 1); - size_t table_offset = dyadic_circuit_size - max_tables_size; + const size_t max_tables_size = + std::min(static_cast(MAX_LOOKUP_TABLES_SIZE), dyadic_circuit_size - 1 - MASKING_OFFSET); + size_t table_offset = dyadic_circuit_size - max_tables_size - MASKING_OFFSET; ASSERT(dyadic_circuit_size > max_tables_size); // Allocate the polynomials containing the actual table data @@ -115,7 +117,8 @@ void DeciderProvingKey_::allocate_table_lookup_polynomials(const Circuit // Allocate the log derivative lookup argument inverse polynomial const size_t lookup_offset = static_cast(circuit.blocks.lookup.trace_offset); - const size_t lookup_inverses_start = std::min(lookup_offset, table_offset); + const size_t masking_offset = (std::min(lookup_offset, table_offset) > MASKING_OFFSET) ? MASKING_OFFSET : 0; + const size_t lookup_inverses_start = std::min(lookup_offset, table_offset) - masking_offset; const size_t lookup_inverses_end = std::min(dyadic_circuit_size, std::max(lookup_offset + circuit.blocks.lookup.get_fixed_size(is_structured), @@ -324,6 +327,7 @@ void DeciderProvingKey_::move_structured_trace_overflow_to_overflow_bloc } template class DeciderProvingKey_; +template class DeciderProvingKey_; template class DeciderProvingKey_; template class DeciderProvingKey_; template class DeciderProvingKey_; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp index 0fdfbfe7e162..c6b5bf1b1c0c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_proving_key.hpp @@ -6,9 +6,9 @@ #include "barretenberg/plonk_honk_shared/execution_trace/ultra_execution_trace.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/stdlib_circuit_builders/mega_zk_flavor.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_rollup_flavor.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_zk_flavor.hpp" #include "barretenberg/trace_to_polynomials/trace_to_polynomials.hpp" namespace bb { @@ -154,7 +154,7 @@ template class DeciderProvingKey_ { PROFILE_THIS_NAME("constructing lookup table polynomials"); construct_lookup_table_polynomials( - proving_key.polynomials.get_tables(), circuit, dyadic_circuit_size); + proving_key.polynomials.get_tables(), circuit, dyadic_circuit_size, MASKING_OFFSET); } { diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp index 2902057f62ab..31f345c83d15 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp @@ -240,7 +240,6 @@ TYPED_TEST(MegaTranscriptTests, VerifierManifestConsistency) // Check consistency between the manifests generated by the prover and verifier auto prover_manifest = prover.transcript->get_manifest(); - prover_manifest.print(); auto verifier_manifest = verifier.transcript->get_manifest();