Skip to content

Commit

Permalink
feat: sumcheck with disabled rows (#10068)
Browse files Browse the repository at this point in the history
ZKFlavors are now running Sumcheck with last 4 rows disabled. To our
knowledge, this is the cheapest approach to mask witness commitments,
their evaluations, and, if necessary, the evaluations of their shifts at
the sumcheck challenge. 

**Note:** The last 4 rows of actual circuits in ECCVM and Translator are becoming
un-constrained. Will be fixed in the follow-up PRs
  • Loading branch information
iakovenkos authored Dec 17, 2024
1 parent 46da3cc commit abd2226
Show file tree
Hide file tree
Showing 20 changed files with 355 additions and 73 deletions.
4 changes: 4 additions & 0 deletions barretenberg/cpp/src/barretenberg/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<size_t>(numeric::get_msb64(num_rows));
const auto num_rows_log2 = static_cast<size_t>(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;
}
Expand Down
9 changes: 6 additions & 3 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Relations>::value;

// Instantiate the BarycentricData needed to extend each Relation Univariate
Expand Down Expand Up @@ -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<size_t>(numeric::get_msb64(num_rows));
const size_t dyadic_num_rows = 1UL << (log_num_rows + (1UL << log_num_rows == num_rows ? 0 : 1));

Expand Down
1 change: 0 additions & 1 deletion barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <cstddef>
#include <vector>
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 <typename FF> 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<FF> 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
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ template <typename BuilderType> class ECCVMRecursiveFlavor_ {
// think these two are not needed for recursive verifier land
// using GrandProductRelations = std::tuple<ECCVMSetRelation<FF>>;
// using LookupRelation = ECCVMLookupRelation<FF>;
static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length<Relations>();
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<Relations>::value;

// Instantiate the BarycentricData needed to extend each Relation Univariate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ template <typename BuilderType> 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<Relations>;

// define the containers for storing the contributions from each relation in Sumcheck
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Relations, HasZK>();
// 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<Relations, HasZK>());
// Re-define ExtendedEdges to account for the incremented MAX_PARTIAL_RELATION_LENGTH
using ExtendedEdges = ProverUnivariates<MAX_PARTIAL_RELATION_LENGTH>;
using SumcheckTupleOfTuplesOfUnivariates = decltype(create_sumcheck_tuple_of_tuples_of_univariates<Relations>());
};

} // namespace bb
30 changes: 25 additions & 5 deletions barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,17 @@ template <typename Flavor> class SumcheckProver {
std::vector<FF> multivariate_challenge;
multivariate_challenge.reserve(multivariate_d);
size_t round_idx = 0;
RowDisablingPolynomial<FF> 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...");
{

Expand All @@ -216,6 +222,7 @@ template <typename Flavor> 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
Expand All @@ -232,7 +239,8 @@ template <typename Flavor> 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<FF>("Sumcheck:u_" + std::to_string(round_idx));
Expand All @@ -242,6 +250,7 @@ template <typename Flavor> 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);
Expand Down Expand Up @@ -590,10 +599,21 @@ template <typename Flavor> 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<FF> row_disabler = RowDisablingPolynomial<FF>();
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<Flavor>) {
Expand Down
Loading

0 comments on commit abd2226

Please sign in to comment.