Skip to content

Commit

Permalink
Merge b9c96ef into 9a1b5b5
Browse files Browse the repository at this point in the history
  • Loading branch information
maramihali authored Sep 24, 2024
2 parents 9a1b5b5 + b9c96ef commit 925b6c7
Show file tree
Hide file tree
Showing 19 changed files with 463 additions and 901 deletions.
6 changes: 3 additions & 3 deletions barretenberg/acir_tests/sol-test/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ const { readFileSync, promises: fsPromises } = fs;
import { spawn } from "child_process";
import { ethers } from "ethers";
import solc from "solc";
import linker from "solc/linker.js";

const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93;
// This excludes the public inputs which are sent separately to the Solidity verifier
const NUMBER_OF_FIELDS_IN_HONK_PROOF = 423;
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1093): This is the size of the proof up to Sumcheck, without public inputs, as the Honk contract does not currently have a PCS.
// This needs to be changed once Shplemini is implemented in the smart contract.
const NUMBER_OF_FIELDS_IN_HONK_PROOF = 303;

// We use the solcjs compiler version in this test, although it is slower than foundry, to run the test end to end
// it simplifies of parallelising the test suite
Expand Down
8 changes: 7 additions & 1 deletion barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,13 @@ void prove_honk(const std::string& bytecodePath, const std::string& witnessPath,
// Construct Honk proof
Prover prover = compute_valid_prover<Flavor>(bytecodePath, witnessPath);
auto proof = prover.construct_proof();
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1093): As the Smart contract doesn't verify the PCS and
// Shplemini is not constant size, we slice the proof up to sumcheck so calculation of public inputs is correct.
// This hack will be subsequently removed.
if constexpr (std::same_as<Flavor, UltraKeccakFlavor>) {
auto num_public_inputs = static_cast<uint32_t>(prover.proving_key->proving_key.num_public_inputs);
proof.erase(proof.begin() + num_public_inputs + 303, proof.end());
}
if (outputPath == "-") {
writeRawBytesToStdout(to_buffer</*include_size=*/true>(proof));
vinfo("proof written to stdout");
Expand Down Expand Up @@ -1494,7 +1501,6 @@ int main(int argc, char* argv[])
std::string output_path = get_option(args, "-o", "./target/contract.sol");
contract(output_path, vk_path);
} else if (command == "contract_ultra_honk") {
vinfo("Warning: Contract incomplete. Do not use in production!");
std::string output_path = get_option(args, "-o", "./target/contract.sol");
contract_honk(output_path, vk_path);
} else if (command == "write_vk") {
Expand Down
235 changes: 2 additions & 233 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/gemini/gemini.cpp
Original file line number Diff line number Diff line change
@@ -1,237 +1,6 @@
#include "gemini.hpp"
#include "barretenberg/common/thread.hpp"

/**
* @brief Protocol for opening several multi-linear polynomials at the same point.
*
*
* m = number of variables
* n = 2ᵐ
* u = (u₀,...,uₘ₋₁)
* f₀, …, fₖ₋₁ = multilinear polynomials,
* g₀, …, gₕ₋₁ = shifted multilinear polynomial,
* Each gⱼ is the left-shift of some f↺ᵢ, and gⱼ points to the same memory location as fᵢ.
* v₀, …, vₖ₋₁, v↺₀, …, v↺ₕ₋₁ = multilinear evalutions s.t. fⱼ(u) = vⱼ, and gⱼ(u) = f↺ⱼ(u) = v↺ⱼ
*
* We use a challenge ρ to create a random linear combination of all fⱼ,
* and actually define A₀ = F + G↺, where
* F = ∑ⱼ ρʲ fⱼ
* G = ∑ⱼ ρᵏ⁺ʲ gⱼ,
* G↺ = is the shift of G
* where fⱼ is normal, and gⱼ is shifted.
* The evaluations are also batched, and
* v = ∑ ρʲ⋅vⱼ + ∑ ρᵏ⁺ʲ⋅v↺ⱼ = F(u) + G↺(u)
*
* The prover then creates the folded polynomials A₀, ..., Aₘ₋₁,
* and opens them at different points, as univariates.
*
* We open A₀ as univariate at r and -r.
* Since A₀ = F + G↺, but the verifier only has commitments to the gⱼs,
* we need to partially evaluate A₀ at both evaluation points.
* As univariate, we have
* A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X
* So we define
* - A₀₊(X) = F(X) + G(X)/r
* - A₀₋(X) = F(X) − G(X)/r
* So that A₀₊(r) = A₀(r) and A₀₋(-r) = A₀(-r).
* The verifier is able to computed the simulated commitments to A₀₊(X) and A₀₋(X)
* since they are linear-combinations of the commitments [fⱼ] and [gⱼ].
*/
#include "gemini_impl.hpp"
namespace bb {
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::prove(
const std::shared_ptr<CommitmentKey<Curve>>& commitment_key,
std::span<Fr> multilinear_challenge,
std::span<Fr> multilinear_evaluations, /* u */
RefSpan<Polynomial> f_polynomials, // unshifted
RefSpan<Polynomial> g_polynomials, // to-be-shifted
std::shared_ptr<NativeTranscript>& transcript)
{
ASSERT(multilinear_evaluations.size() == f_polynomials.size() + g_polynomials.size());
const size_t log_n = multilinear_challenge.size();
const size_t n = 1 << log_n;

Fr rho = transcript->template get_challenge<Fr>("rho");
std::vector<Fr> rhos = gemini::powers_of_rho(rho, multilinear_evaluations.size());

// Compute batched multivariate evaluation
Fr batched_evaluation = Fr::zero();
for (size_t i = 0; i < rhos.size(); ++i) {
batched_evaluation += multilinear_evaluations[i] * rhos[i];
}

// Compute batched polynomials
Polynomial batched_unshifted(n);
Polynomial batched_to_be_shifted = Polynomial::shiftable(1 << log_n);

const size_t num_unshifted = f_polynomials.size();
const size_t num_to_be_shifted = g_polynomials.size();
for (size_t i = 0; i < num_unshifted; i++) {
batched_unshifted.add_scaled(f_polynomials[i], rhos[i]);
}
for (size_t i = 0; i < num_to_be_shifted; i++) {
batched_to_be_shifted.add_scaled(g_polynomials[i], rhos[num_unshifted + i]);
}

auto fold_polynomials =
compute_fold_polynomials(multilinear_challenge, std::move(batched_unshifted), std::move(batched_to_be_shifted));

for (size_t l = 0; l < log_n - 1; l++) {
transcript->send_to_verifier("Gemini:FOLD_" + std::to_string(l + 1),
commitment_key->commit(fold_polynomials[l + 2]));
}
const Fr r_challenge = transcript->template get_challenge<Fr>("Gemini:r");
std::vector<Claim> claims =
compute_fold_polynomial_evaluations(multilinear_challenge, std::move(fold_polynomials), r_challenge);

for (size_t l = 1; l <= log_n; l++) {
transcript->send_to_verifier("Gemini:a_" + std::to_string(l), claims[l].opening_pair.evaluation);
}

return claims;
};

/**
* @brief Computes d-1 fold polynomials Fold_i, i = 1, ..., d-1
*
* @param mle_opening_point multilinear opening point 'u'
* @param batched_unshifted F(X) = ∑ⱼ ρʲ fⱼ(X)
* @param batched_to_be_shifted G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)
* @return std::vector<Polynomial>
*/
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Polynomial> GeminiProver_<Curve>::compute_fold_polynomials(
std::span<const Fr> mle_opening_point, Polynomial&& batched_unshifted, Polynomial&& batched_to_be_shifted)
{
const size_t num_variables = mle_opening_point.size(); // m

const size_t num_threads = get_num_cpus_pow2();
constexpr size_t efficient_operations_per_thread = 64; // A guess of the number of operation for which there
// would be a point in sending them to a separate thread

// Allocate space for m+1 Fold polynomials
//
// The first two are populated here with the batched unshifted and to-be-shifted polynomial respectively.
// They will eventually contain the full batched polynomial A₀ partially evaluated at the challenges r,-r.
// This function populates the other m-1 polynomials with the foldings of A₀.
std::vector<Polynomial> fold_polynomials;
fold_polynomials.reserve(num_variables + 1);

// F(X) = ∑ⱼ ρʲ fⱼ(X) and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)
Polynomial& batched_F = fold_polynomials.emplace_back(std::move(batched_unshifted));
Polynomial& batched_G = fold_polynomials.emplace_back(std::move(batched_to_be_shifted));
constexpr size_t offset_to_folded = 2; // Offset because of F an G
// A₀(X) = F(X) + G↺(X) = F(X) + G(X)/X.
Polynomial A_0 = batched_F;
A_0 += batched_G.shifted();

// Allocate everything before parallel computation
for (size_t l = 0; l < num_variables - 1; ++l) {
// size of the previous polynomial/2
const size_t n_l = 1 << (num_variables - l - 1);

// A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
fold_polynomials.emplace_back(Polynomial(n_l));
}

// A_l = Aₗ(X) is the polynomial being folded
// in the first iteration, we take the batched polynomial
// in the next iteration, it is the previously folded one
auto A_l = A_0.data();
for (size_t l = 0; l < num_variables - 1; ++l) {
// size of the previous polynomial/2
const size_t n_l = 1 << (num_variables - l - 1);

// Use as many threads as it is useful so that 1 thread doesn't process 1 element, but make sure that there is
// at least 1
size_t num_used_threads = std::min(n_l / efficient_operations_per_thread, num_threads);
num_used_threads = num_used_threads ? num_used_threads : 1;
size_t chunk_size = n_l / num_used_threads;
size_t last_chunk_size = (n_l % chunk_size) ? (n_l % num_used_threads) : chunk_size;

// Openning point is the same for all
const Fr u_l = mle_opening_point[l];

// A_l_fold = Aₗ₊₁(X) = (1-uₗ)⋅even(Aₗ)(X) + uₗ⋅odd(Aₗ)(X)
auto A_l_fold = fold_polynomials[l + offset_to_folded].data();

parallel_for(num_used_threads, [&](size_t i) {
size_t current_chunk_size = (i == (num_used_threads - 1)) ? last_chunk_size : chunk_size;
for (std::ptrdiff_t j = (std::ptrdiff_t)(i * chunk_size);
j < (std::ptrdiff_t)((i * chunk_size) + current_chunk_size);
j++) {
// fold(Aₗ)[j] = (1-uₗ)⋅even(Aₗ)[j] + uₗ⋅odd(Aₗ)[j]
// = (1-uₗ)⋅Aₗ[2j] + uₗ⋅Aₗ[2j+1]
// = Aₗ₊₁[j]
A_l_fold[j] = A_l[j << 1] + u_l * (A_l[(j << 1) + 1] - A_l[j << 1]);
}
});
// set Aₗ₊₁ = Aₗ for the next iteration
A_l = A_l_fold;
}

return fold_polynomials;
};

/**
* @brief Computes/aggragates d+1 Fold polynomials and their opening pairs (challenge, evaluation)
*
* @details This function assumes that, upon input, last d-1 entries in fold_polynomials are Fold_i.
* The first two entries are assumed to be, respectively, the batched unshifted and batched to-be-shifted
* polynomials F(X) = ∑ⱼ ρʲfⱼ(X) and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X). This function completes the computation
* of the first two Fold polynomials as F + G/r and F - G/r. It then evaluates each of the d+1
* fold polynomials at, respectively, the points r, rₗ = r^{2ˡ} for l = 0, 1, ..., d-1.
*
* @param mle_opening_point u = (u₀,...,uₘ₋₁) is the MLE opening point
* @param fold_polynomials vector of polynomials whose first two elements are F(X) = ∑ⱼ ρʲfⱼ(X)
* and G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X), and the next d-1 elements are Fold_i, i = 1, ..., d-1.
* @param r_challenge univariate opening challenge
*/
template <typename Curve>
std::vector<typename GeminiProver_<Curve>::Claim> GeminiProver_<Curve>::compute_fold_polynomial_evaluations(
std::span<const Fr> mle_opening_point, std::vector<Polynomial>&& fold_polynomials, const Fr& r_challenge)
{
const size_t num_variables = mle_opening_point.size(); // m

Polynomial& batched_F = fold_polynomials[0]; // F(X) = ∑ⱼ ρʲ fⱼ(X)
Polynomial& batched_G = fold_polynomials[1]; // G(X) = ∑ⱼ ρᵏ⁺ʲ gⱼ(X)

// Compute univariate opening queries rₗ = r^{2ˡ} for l = 0, 1, ..., m-1
std::vector<Fr> r_squares = gemini::powers_of_evaluation_challenge(r_challenge, num_variables);

// Compute G/r
Fr r_inv = r_challenge.invert();
batched_G *= r_inv;

// Construct A₀₊ = F + G/r and A₀₋ = F - G/r in place in fold_polynomials
Polynomial tmp = batched_F;
Polynomial& A_0_pos = fold_polynomials[0];

// A₀₊(X) = F(X) + G(X)/r, s.t. A₀₊(r) = A₀(r)
A_0_pos += batched_G;

// Perform a swap so that tmp = G(X)/r and A_0_neg = F(X)
std::swap(tmp, batched_G);
Polynomial& A_0_neg = fold_polynomials[1];

// A₀₋(X) = F(X) - G(X)/r, s.t. A₀₋(-r) = A₀(-r)
A_0_neg -= tmp;

std::vector<Claim> opening_claims;
opening_claims.reserve(num_variables + 1);

// Compute first opening pair {r, A₀(r)}
Fr evaluation = fold_polynomials[0].evaluate(r_challenge);
opening_claims.emplace_back(
Claim{ fold_polynomials[0], { r_challenge, fold_polynomials[0].evaluate(r_challenge) } });
// Compute the remaining m opening pairs {−r^{2ˡ}, Aₗ(−r^{2ˡ})}, l = 0, ..., m-1.
for (size_t l = 0; l < num_variables; ++l) {
evaluation = fold_polynomials[l + 1].evaluate(-r_squares[l]);
opening_claims.emplace_back(Claim{ fold_polynomials[l + 1], { -r_squares[l], evaluation } });
}

return opening_claims;
};
template class GeminiProver_<curve::BN254>;
template class GeminiProver_<curve::Grumpkin>;
} // namespace bb
}; // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,22 @@ template <typename Curve> class GeminiProver_ {
using Claim = ProverOpeningClaim<Curve>;

public:
static std::vector<Polynomial> compute_fold_polynomials(std::span<const Fr> multilinear_evaluations,
static std::vector<Polynomial> compute_fold_polynomials(const size_t log_N,
std::span<const Fr> multilinear_challenge,
Polynomial&& batched_unshifted,
Polynomial&& batched_to_be_shifted);

static std::vector<Claim> compute_fold_polynomial_evaluations(std::span<const Fr> multilinear_evaluations,
static std::vector<Claim> compute_fold_polynomial_evaluations(const size_t log_N,
std::vector<Polynomial>&& fold_polynomials,
const Fr& r_challenge);

static std::vector<Claim> prove(const std::shared_ptr<CommitmentKey<Curve>>& commitment_key,
std::span<Fr> multilinear_challenge,
std::span<Fr> multilinear_evaluations,
template <typename Transcript>
static std::vector<Claim> prove(const Fr circuit_size,
RefSpan<Polynomial> f_polynomials,
RefSpan<Polynomial> g_polynomials,
std::shared_ptr<NativeTranscript>& transcript);
std::span<Fr> multilinear_challenge,
const std::shared_ptr<CommitmentKey<Curve>>& commitment_key,
const std::shared_ptr<Transcript>& transcript);
}; // namespace bb

template <typename Curve> class GeminiVerifier_ {
Expand All @@ -134,7 +136,7 @@ template <typename Curve> class GeminiVerifier_ {
* (Cⱼ, Aⱼ(-r^{2ʲ}), -r^{2}), j = [1, ..., m-1]
*/
static std::vector<OpeningClaim<Curve>> reduce_verification(std::span<Fr> multilinear_challenge,
std::span<Fr> multilinear_evaluations, /* u */
std::span<Fr> multilinear_evaluations,
RefSpan<GroupElement> unshifted_commitments,
RefSpan<GroupElement> to_be_shifted_commitments,
auto& transcript)
Expand Down Expand Up @@ -172,7 +174,7 @@ template <typename Curve> class GeminiVerifier_ {
const std::vector<Fr> evaluations = get_gemini_evaluations(num_variables, transcript);
// Compute evaluation A₀(r)
auto a_0_pos = compute_gemini_batched_univariate_evaluation(
batched_evaluation, multilinear_challenge, r_squares, evaluations);
num_variables, batched_evaluation, multilinear_challenge, r_squares, evaluations);

// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] + r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
// C₀_r_pos = ∑ⱼ ρʲ⋅[fⱼ] - r⁻¹⋅∑ⱼ ρᵏ⁺ʲ [gⱼ]
Expand Down Expand Up @@ -210,7 +212,7 @@ template <typename Curve> class GeminiVerifier_ {
{
std::vector<Fr> gemini_evaluations;
gemini_evaluations.reserve(log_circuit_size);
for (size_t i = 0; i < log_circuit_size; ++i) {
for (size_t i = 1; i <= log_circuit_size; ++i) {
const Fr evaluation = transcript->template receive_from_prover<Fr>("Gemini:a_" + std::to_string(i));
gemini_evaluations.emplace_back(evaluation);
}
Expand Down Expand Up @@ -239,12 +241,13 @@ template <typename Curve> class GeminiVerifier_ {
* @param fold_polynomial_evals Evaluations \f$ A_{i-1}(-r^{2^{i-1}}) \f$.
* @return Evaluation \f$ A_0(r) \f$.
*/
static Fr compute_gemini_batched_univariate_evaluation(Fr& batched_eval_accumulator,
static Fr compute_gemini_batched_univariate_evaluation(size_t evaluation_point_size,
Fr& batched_eval_accumulator,
std::span<const Fr> evaluation_point,
std::span<const Fr> challenge_powers,
std::span<const Fr> fold_polynomial_evals)
{
const size_t num_variables = evaluation_point.size();
const size_t num_variables = evaluation_point_size;

const auto& evals = fold_polynomial_evals;

Expand Down
Loading

0 comments on commit 925b6c7

Please sign in to comment.