Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,15 @@ std::shared_ptr<plonk::proving_key> UltraHonkComposerHelper<CircuitConstructor>:

construct_lagrange_selector_forms(circuit_constructor, circuit_proving_key.get());

enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get());
// TODO(#217)(luke): Naively enforcing non-zero selectors for Honk will result in some relations not being
// satisfied.
// enforce_nonzero_polynomial_selectors(circuit_constructor, circuit_proving_key.get());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do this, we'll have to add the zero knowledge relation before we can create a valid proof, since selectors could easily be zero.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a very good point. We need to make sure our tests are not passing trivially with zero selectors until we decide how to proceed. Maybe we should prioritize issue #217 in the near future.


compute_honk_generalized_sigma_permutations<CircuitConstructor::program_width>(circuit_constructor,
circuit_proving_key.get());

compute_first_and_last_lagrange_polynomials(circuit_proving_key.get());

const size_t subgroup_size = circuit_proving_key->circuit_size;

polynomial poly_q_table_column_1(subgroup_size);
Expand Down
103 changes: 0 additions & 103 deletions cpp/src/barretenberg/honk/composer/standard_honk_composer.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,109 +304,6 @@ TEST(StandardHonkComposer, VerificationKeyCreation)
composer.circuit_constructor.selectors.size() + composer.num_wires * 2 + 2);
}

/**
* @brief A test taking sumcheck relations and applying them to the witness and selector polynomials to ensure that the
* realtions are correct.
*
* TODO(Kesha): We'll have to update this function once we add zk, since the relation will be incorrect for he first few
* indices
*
*/
TEST(StandardHonkComposer, SumcheckRelationCorrectness)
{
// Create a composer and a dummy circuit with a few gates
StandardHonkComposer composer = StandardHonkComposer();
static const size_t num_wires = StandardHonkComposer::num_wires;
fr a = fr::one();
// Using the public variable to check that public_input_delta is computed and added to the relation correctly
uint32_t a_idx = composer.add_public_variable(a);
fr b = fr::one();
fr c = a + b;
fr d = a + c;
uint32_t b_idx = composer.add_variable(b);
uint32_t c_idx = composer.add_variable(c);
uint32_t d_idx = composer.add_variable(d);
for (size_t i = 0; i < 16; i++) {
composer.create_add_gate({ a_idx, b_idx, c_idx, fr::one(), fr::one(), fr::neg_one(), fr::zero() });
composer.create_add_gate({ d_idx, c_idx, a_idx, fr::one(), fr::neg_one(), fr::neg_one(), fr::zero() });
}
// Create a prover (it will compute proving key and witness)
auto prover = composer.create_prover();

// Generate beta and gamma
fr beta = fr::random_element();
fr gamma = fr::random_element();

// Compute public input delta
const auto public_inputs = composer.circuit_constructor.get_public_inputs();
auto public_input_delta =
honk::compute_public_input_delta<fr>(public_inputs, beta, gamma, prover.key->circuit_size);

sumcheck::RelationParameters<fr> params{
.beta = beta,
.gamma = gamma,
.public_input_delta = public_input_delta,
};

constexpr size_t num_polynomials = proof_system::honk::StandardArithmetization::NUM_POLYNOMIALS;
// Compute grand product polynomial
polynomial z_perm_poly =
prover_library::compute_permutation_grand_product<num_wires>(prover.key, prover.wire_polynomials, beta, gamma);

// Create an array of spans to the underlying polynomials to more easily
// get the transposition.
// Ex: polynomial_spans[3][i] returns the i-th coefficient of the third polynomial
// in the list below
std::array<std::span<const fr>, num_polynomials> evaluations_array;

using POLYNOMIAL = proof_system::honk::StandardArithmetization::POLYNOMIAL;
evaluations_array[POLYNOMIAL::W_L] = prover.wire_polynomials[0];
evaluations_array[POLYNOMIAL::W_R] = prover.wire_polynomials[1];
evaluations_array[POLYNOMIAL::W_O] = prover.wire_polynomials[2];
evaluations_array[POLYNOMIAL::Z_PERM] = z_perm_poly;
evaluations_array[POLYNOMIAL::Z_PERM_SHIFT] = z_perm_poly.shifted();
evaluations_array[POLYNOMIAL::Q_M] = prover.key->polynomial_store.get("q_m_lagrange");
evaluations_array[POLYNOMIAL::Q_L] = prover.key->polynomial_store.get("q_1_lagrange");
evaluations_array[POLYNOMIAL::Q_R] = prover.key->polynomial_store.get("q_2_lagrange");
evaluations_array[POLYNOMIAL::Q_O] = prover.key->polynomial_store.get("q_3_lagrange");
evaluations_array[POLYNOMIAL::Q_C] = prover.key->polynomial_store.get("q_c_lagrange");
evaluations_array[POLYNOMIAL::SIGMA_1] = prover.key->polynomial_store.get("sigma_1_lagrange");
evaluations_array[POLYNOMIAL::SIGMA_2] = prover.key->polynomial_store.get("sigma_2_lagrange");
evaluations_array[POLYNOMIAL::SIGMA_3] = prover.key->polynomial_store.get("sigma_3_lagrange");
evaluations_array[POLYNOMIAL::ID_1] = prover.key->polynomial_store.get("id_1_lagrange");
evaluations_array[POLYNOMIAL::ID_2] = prover.key->polynomial_store.get("id_2_lagrange");
evaluations_array[POLYNOMIAL::ID_3] = prover.key->polynomial_store.get("id_3_lagrange");
evaluations_array[POLYNOMIAL::LAGRANGE_FIRST] = prover.key->polynomial_store.get("L_first_lagrange");
evaluations_array[POLYNOMIAL::LAGRANGE_LAST] = prover.key->polynomial_store.get("L_last_lagrange");

// Construct the round for applying sumcheck relations and results for storing computed results
auto relations = std::tuple(honk::sumcheck::ArithmeticRelation<fr>(),
honk::sumcheck::GrandProductComputationRelation<fr>(),
honk::sumcheck::GrandProductInitializationRelation<fr>());

fr result = 0;
for (size_t i = 0; i < prover.key->circuit_size; i++) {
// Compute an array containing all the evaluations at a given row i
std::array<fr, num_polynomials> evaluations_at_index_i;
for (size_t j = 0; j < num_polynomials; ++j) {
evaluations_at_index_i[j] = evaluations_array[j][i];
}

// For each relation, call the `accumulate_relation_evaluation` over all witness/selector values at the
// i-th row/vertex of the hypercube.
// We use ASSERT_EQ instead of EXPECT_EQ so that the tests stops at the first index at which the result is not
// 0, since result = 0 + C(transposed), which we expect will equal 0.
std::get<0>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params);
ASSERT_EQ(result, 0);

std::get<1>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params);
ASSERT_EQ(result, 0);

std::get<2>(relations).add_full_relation_value_contribution(result, evaluations_at_index_i, params);
ASSERT_EQ(result, 0);
}
}

TEST(StandardHonkComposer, BaseCase)
{
auto composer = StandardHonkComposer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,25 @@ std::vector<uint32_t> add_variables(auto& composer, std::vector<fr> variables)
* @param honk_prover
* @param plonk_prover
*/
// NOTE: Currently only checking witness polynomials (wires, sorted lists) and table polys. The permutation polys
// are computed differently between plonk and honk and we do not enforce non-zero selectors in Honk so the final
// element in the selectors will disagree.
void verify_consistency(honk::UltraProver& honk_prover, plonk::UltraProver& plonk_prover)
{
// Check that all lagrange polys agree
auto& honk_store = honk_prover.key->polynomial_store;
auto& plonk_store = plonk_prover.key->polynomial_store;
for (auto& entry : honk_store) {
std::string key = entry.first;
if (plonk_store.contains(key)) {
bool is_sorted_table = (key.find("s_") != std::string::npos);
bool is_table = (key.find("table_value_") != std::string::npos);
if (plonk_store.contains(key) && (is_sorted_table || is_table)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you not checking wires any more?

Copy link
Copy Markdown
Collaborator Author

@ledwards2225 ledwards2225 Apr 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wires are checked just below this. It has to be done in a separate loop since the wires are owned directly by the prover in Honk but they are still stored in the proving_key for Plonk. I've added a comment to clarify.

ASSERT_EQ(honk_store.get(key), plonk_store.get(key));
}
}

// Check that all wires agree
// Note: for Honk, wires are owned directly by the prover. For Plonk they are stored in the key.
for (size_t i = 0; i < 4; ++i) {
std::string label = "w_" + std::to_string(i + 1) + "_lagrange";
ASSERT_EQ(honk_prover.wire_polynomials[i], plonk_prover.key->polynomial_store.get(label));
Expand Down
56 changes: 55 additions & 1 deletion cpp/src/barretenberg/honk/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct StandardArithmetization {
* This separation must be maintained to allow for programmatic access, but the ordering of the
* polynomials can be permuted within each category if necessary. Polynomials can also be added
* or removed (assuming consistency with the prover algorithm) but the constants describing the
* number of poynomials in each category must be manually updated.
* number of polynomials in each category must be manually updated.
*
*/
enum POLYNOMIAL {
Expand Down Expand Up @@ -187,4 +187,58 @@ struct StandardHonk {
return output;
}
};

struct UltraArithmetization {
/**
* @brief All of the multivariate polynomials used by the Ultra Honk Prover.
* @details The polynomials are broken into three categories: precomputed, witness, and shifted.
* This separation must be maintained to allow for programmatic access, but the ordering of the
* polynomials can be permuted within each category if necessary. Polynomials can also be added
* or removed (assuming consistency with the prover algorithm) but the constants describing the
* number of polynomials in each category must be manually updated.
*
*/
enum POLYNOMIAL {
/* --- PRECOMPUTED POLYNOMIALS --- */
Q_C,
Q_L,
Q_R,
Q_O,
Q_4,
Q_M,
QARITH,
QSORT,
QELLIPTIC,
QAUX,
QLOOKUPTYPE,
SIGMA_1,
SIGMA_2,
SIGMA_3,
SIGMA_4,
ID_1,
ID_2,
ID_3,
ID_4,
LAGRANGE_FIRST,
LAGRANGE_LAST, // = LAGRANGE_N-1 whithout ZK, but can be less
/* --- WITNESS POLYNOMIALS --- */
W_L,
W_R,
W_O,
W_4,
S_1,
S_2,
S_3,
S_4,
Z_PERM,
Z_LOOKUP,
/* --- SHIFTED POLYNOMIALS --- */
W_1_SHIFT,
Comment thread
ledwards2225 marked this conversation as resolved.
W_4_SHIFT,
Z_PERM_SHIFT,
Z_LOOKUP_SHIFT,
/* --- --- */
COUNT // for programmatic determination of NUM_POLYNOMIALS
};
};
} // namespace proof_system::honk
10 changes: 5 additions & 5 deletions cpp/src/barretenberg/honk/proof_system/prover_library.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "prover_library.hpp"
#include "barretenberg/plonk/proof_system/types/prover_settings.hpp"
#include <span>
#include <string>

namespace proof_system::honk::prover_library {

Expand Down Expand Up @@ -55,19 +56,18 @@ Polynomial compute_permutation_grand_product(std::shared_ptr<plonk::proving_key>
// Populate wire and permutation polynomials
std::array<std::span<const Fr>, program_width> wires;
std::array<std::span<const Fr>, program_width> sigmas;
std::array<std::span<const Fr>, program_width> ids;
for (size_t i = 0; i < program_width; ++i) {
std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange";
wires[i] = wire_polynomials[i];
sigmas[i] = key->polynomial_store.get(sigma_id);
sigmas[i] = key->polynomial_store.get("sigma_" + std::to_string(i + 1) + "_lagrange");
ids[i] = key->polynomial_store.get("id_" + std::to_string(i + 1) + "_lagrange");
}

// Step (1)
// TODO(#222)(kesha): Change the order to engage automatic prefetching and get rid of redundant computation
for (size_t i = 0; i < key->circuit_size; ++i) {
for (size_t k = 0; k < program_width; ++k) {
// Note(luke): this idx could be replaced by proper ID polys if desired
Fr idx = k * key->circuit_size + i;
numerator_accumulator[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ
numerator_accumulator[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ
denominator_accumulator[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ
}
}
Expand Down
13 changes: 8 additions & 5 deletions cpp/src/barretenberg/honk/proof_system/prover_library.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ template <class FF> class ProverLibraryTests : public testing::Test {
// can simply be random. We're not interested in the particular properties of the result.
std::vector<Polynomial> wires;
std::vector<Polynomial> sigmas;
std::vector<Polynomial> ids;
for (size_t i = 0; i < program_width; ++i) {
wires.emplace_back(get_random_polynomial(num_gates));
sigmas.emplace_back(get_random_polynomial(num_gates));
ids.emplace_back(get_random_polynomial(num_gates));

// Add sigma polys to proving_key; to be used by the prover in constructing it's own z_perm
std::string sigma_id = "sigma_" + std::to_string(i + 1) + "_lagrange";
proving_key->polynomial_store.put(sigma_id, Polynomial{ sigmas[i] });
// Add sigma/ID polys to proving_key; to be used by the prover in constructing it's own z_perm
std::string sigma_label = "sigma_" + std::to_string(i + 1) + "_lagrange";
proving_key->polynomial_store.put(sigma_label, Polynomial{ sigmas[i] });
std::string id_label = "id_" + std::to_string(i + 1) + "_lagrange";
proving_key->polynomial_store.put(id_label, Polynomial{ ids[i] });
}

// Get random challenges
Expand Down Expand Up @@ -108,8 +112,7 @@ template <class FF> class ProverLibraryTests : public testing::Test {
// Step (1)
for (size_t i = 0; i < proving_key->circuit_size; ++i) {
for (size_t k = 0; k < program_width; ++k) {
FF idx = k * proving_key->circuit_size + i; // id_k[i]
numererator_accum[k][i] = wires[k][i] + (idx * beta) + gamma; // w_k(i) + β.(k*n+i) + γ
numererator_accum[k][i] = wires[k][i] + (ids[k][i] * beta) + gamma; // w_k(i) + β.id_k(i) + γ
denominator_accum[k][i] = wires[k][i] + (sigmas[k][i] * beta) + gamma; // w_k(i) + β.σ_k(i) + γ
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,91 @@ template <typename FF> class GrandProductComputationRelation {
(w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma));
};
};

// TODO(luke): With Cody's Flavor work it should be easier to create a simple templated relation
// for handling arbitrary width. For now I'm duplicating the width 3 logic for width 4.
template <typename FF> class UltraGrandProductComputationRelation {
public:
// 1 + polynomial degree of this relation
static constexpr size_t RELATION_LENGTH = 6;
using MULTIVARIATE = proof_system::honk::UltraArithmetization::POLYNOMIAL;

/**
* @brief Compute contribution of the permutation relation for a given edge (internal function)
*
* @details This the relation confirms faithful calculation of the grand
* product polynomial Z_perm.
*
* @param evals transformed to `evals + C(extended_edges(X)...)*scaling_factor`
* @param extended_edges an std::array containing the fully extended Univariate edges.
* @param parameters contains beta, gamma, and public_input_delta, ....
* @param scaling_factor optional term to scale the evaluation before adding to evals.
*/
inline void add_edge_contribution(Univariate<FF, RELATION_LENGTH>& evals,
const auto& extended_edges,
const RelationParameters<FF>& relation_parameters,
const FF& scaling_factor) const
{
const auto& beta = relation_parameters.beta;
const auto& gamma = relation_parameters.gamma;
const auto& public_input_delta = relation_parameters.public_input_delta;

auto w_1 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::W_L]);
auto w_2 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::W_R]);
auto w_3 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::W_O]);
auto w_4 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::W_4]);
auto sigma_1 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::SIGMA_1]);
auto sigma_2 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::SIGMA_2]);
auto sigma_3 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::SIGMA_3]);
auto sigma_4 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::SIGMA_4]);
auto id_1 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::ID_1]);
auto id_2 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::ID_2]);
auto id_3 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::ID_3]);
auto id_4 = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::ID_4]);
auto z_perm = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::Z_PERM]);
auto z_perm_shift = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::Z_PERM_SHIFT]);
auto lagrange_first = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::LAGRANGE_FIRST]);
auto lagrange_last = UnivariateView<FF, RELATION_LENGTH>(extended_edges[MULTIVARIATE::LAGRANGE_LAST]);

// Contribution (1)
evals += (((z_perm + lagrange_first) * (w_1 + id_1 * beta + gamma) * (w_2 + id_2 * beta + gamma) *
(w_3 + id_3 * beta + gamma) * (w_4 + id_4 * beta + gamma)) -
((z_perm_shift + lagrange_last * public_input_delta) * (w_1 + sigma_1 * beta + gamma) *
(w_2 + sigma_2 * beta + gamma) * (w_3 + sigma_3 * beta + gamma) * (w_4 + sigma_4 * beta + gamma))) *
scaling_factor;
};

void add_full_relation_value_contribution(FF& full_honk_relation_value,
auto& purported_evaluations,
const RelationParameters<FF>& relation_parameters) const
{
const auto& beta = relation_parameters.beta;
const auto& gamma = relation_parameters.gamma;
const auto& public_input_delta = relation_parameters.public_input_delta;

auto w_1 = purported_evaluations[MULTIVARIATE::W_L];
auto w_2 = purported_evaluations[MULTIVARIATE::W_R];
auto w_3 = purported_evaluations[MULTIVARIATE::W_O];
auto w_4 = purported_evaluations[MULTIVARIATE::W_4];
auto sigma_1 = purported_evaluations[MULTIVARIATE::SIGMA_1];
auto sigma_2 = purported_evaluations[MULTIVARIATE::SIGMA_2];
auto sigma_3 = purported_evaluations[MULTIVARIATE::SIGMA_3];
auto sigma_4 = purported_evaluations[MULTIVARIATE::SIGMA_4];
auto id_1 = purported_evaluations[MULTIVARIATE::ID_1];
auto id_2 = purported_evaluations[MULTIVARIATE::ID_2];
auto id_3 = purported_evaluations[MULTIVARIATE::ID_3];
auto id_4 = purported_evaluations[MULTIVARIATE::ID_4];
auto z_perm = purported_evaluations[MULTIVARIATE::Z_PERM];
auto z_perm_shift = purported_evaluations[MULTIVARIATE::Z_PERM_SHIFT];
auto lagrange_first = purported_evaluations[MULTIVARIATE::LAGRANGE_FIRST];
auto lagrange_last = purported_evaluations[MULTIVARIATE::LAGRANGE_LAST];

// Contribution (1)
full_honk_relation_value +=
((z_perm + lagrange_first) * (w_1 + beta * id_1 + gamma) * (w_2 + beta * id_2 + gamma) *
(w_3 + beta * id_3 + gamma) * (w_4 + beta * id_4 + gamma) -
(z_perm_shift + lagrange_last * public_input_delta) * (w_1 + beta * sigma_1 + gamma) *
(w_2 + beta * sigma_2 + gamma) * (w_3 + beta * sigma_3 + gamma) * (w_4 + beta * sigma_4 + gamma));
};
};
} // namespace proof_system::honk::sumcheck
Loading