Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mega zk features #9774

Merged
merged 37 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2c37dc4
shplemini supports opening of libra masking univariates
iakovenkos Oct 28, 2024
7bbacc2
clean-up
iakovenkos Oct 29, 2024
c78ed6e
de-coupled sumcheck from zk sumcheck data
iakovenkos Oct 30, 2024
fe93b4d
eccvm native proves/verifies evals of masking polys
iakovenkos Oct 30, 2024
df7f731
debugging eccvm recursive
iakovenkos Oct 31, 2024
0b3e50a
debugggging still
iakovenkos Oct 31, 2024
525ae12
Merge branch 'master' into si/zk-sumcheck-plus-shplemini
iakovenkos Oct 31, 2024
4298d52
eccvm recursive fixed
iakovenkos Oct 31, 2024
bf468eb
opening proofs for libra polynomials in translator
iakovenkos Nov 4, 2024
ab9b81e
translator relation degree changes reverted
iakovenkos Nov 4, 2024
aca70fa
some clean-up
iakovenkos Nov 4, 2024
b7584ba
removed redundant line
iakovenkos Nov 4, 2024
6403477
clean-up
iakovenkos Nov 4, 2024
bec76b9
Merge branch 'master' into si/zk-sumcheck-plus-shplemini
iakovenkos Nov 4, 2024
8a1e95f
fix: link zero constraint relation in translator
iakovenkos Nov 4, 2024
2504556
fix bb native tests
iakovenkos Nov 5, 2024
d18f0c2
Merge branch 'master' into si/zk-sumcheck-plus-shplemini
iakovenkos Nov 5, 2024
937af2a
clean up
iakovenkos Nov 5, 2024
6824085
Merge branch 'si/zk-sumcheck-plus-shplemini' of github.com:AztecProto…
iakovenkos Nov 5, 2024
95d4160
fix build
iakovenkos Nov 5, 2024
6d35080
evaluation proofs for libra masking univariates in Mega + transcript …
iakovenkos Nov 5, 2024
ac0afe5
megahonk tests + bench
iakovenkos Nov 6, 2024
b7b9fae
mega with k recursive
iakovenkos Nov 6, 2024
db32e48
fixed mega transcript test + oink headers clean-up
iakovenkos Nov 6, 2024
69b913e
clean-up after review
iakovenkos Nov 6, 2024
ed57e42
Merge branch 'master' into si/zk-sumcheck-plus-shplemini
iakovenkos Nov 6, 2024
1775d41
Merge branch 'master' into si/zk-sumcheck-plus-shplemini
iakovenkos Nov 7, 2024
e8c691a
resolve conflicts with 'master'
iakovenkos Nov 7, 2024
2dc2170
Merge branch 'si/zk-sumcheck-plus-shplemini' into si/zk-sumcheck-mega
iakovenkos Nov 7, 2024
3157236
clean up and renaming MegaFlavorWithZK to MegaZKFlavor
iakovenkos Nov 7, 2024
6a32b27
Merge branch 'master' into si/zk-sumcheck-mega
iakovenkos Nov 7, 2024
84bf601
more clean up after merging
iakovenkos Nov 7, 2024
f2d4eb0
cleaning
iakovenkos Nov 7, 2024
a94d29a
Merge branch 'master' into si/zk-sumcheck-mega
iakovenkos Nov 7, 2024
cef08d4
flavor clean-up
iakovenkos Nov 7, 2024
3fe9ed7
Merge branch 'si/zk-sumcheck-mega' of github.com:AztecProtocol/aztec-…
iakovenkos Nov 7, 2024
c24adea
addressing comments
iakovenkos Nov 7, 2024
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
@@ -0,0 +1,51 @@
#include <benchmark/benchmark.h>

#include "barretenberg/benchmark/ultra_bench/mock_circuits.hpp"
#include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp"

using namespace benchmark;
using namespace bb;

/**
* @brief Benchmark: Construction of a Ultra Honk proof for a circuit determined by the provided circuit function
*/
static void construct_proof_megahonk_zk(State& state,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that instead of duplicating the mega honk benchmark file you could just template the MegaHonk benchmark

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried honestly, but it's not as nice as with the typed test suites, so prob we could keep two separate bench files for now (and maybe delete the one without zk, when we are done with zk)

void (*test_circuit_function)(MegaCircuitBuilder&, size_t)) noexcept
{
size_t num_iterations = 10; // 10x the circuit
bb::mock_circuits::construct_proof_with_specified_num_iterations<MegaZKProver>(
state, test_circuit_function, num_iterations);
}

/**
* @brief Benchmark: Construction of a Ultra Plonk proof with 2**n gates
*/
static void construct_proof_megahonk_power_of_2_zk(State& state) noexcept
{
auto log2_of_gates = static_cast<size_t>(state.range(0));
bb::mock_circuits::construct_proof_with_specified_num_iterations<MegaZKProver>(
state, &bb::mock_circuits::generate_basic_arithmetic_circuit<MegaCircuitBuilder>, log2_of_gates);
}

// Define benchmarks

// This exists due to an issue where get_row was blowing up in time
BENCHMARK_CAPTURE(construct_proof_megahonk_zk, sha256, &stdlib::generate_sha256_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);
BENCHMARK_CAPTURE(construct_proof_megahonk_zk, keccak, &stdlib::generate_keccak_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);
BENCHMARK_CAPTURE(construct_proof_megahonk_zk,
ecdsa_verification,
&stdlib::generate_ecdsa_verification_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);
BENCHMARK_CAPTURE(construct_proof_megahonk_zk,
merkle_membership,
&stdlib::generate_merkle_membership_test_circuit<MegaCircuitBuilder>)
->Unit(kMillisecond);

BENCHMARK(construct_proof_megahonk_power_of_2_zk)
// 2**15 gates to 2**20 gates
->DenseRange(15, 20)
->Unit(kMillisecond);

BENCHMARK_MAIN();
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,42 @@ TYPED_TEST(KZGTest, single)
EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true);
}

/**
* @brief Test opening proof of a polynomial given by its evaluations at \f$ i = 0, \ldots, n \f$. Should only be used
* for small values of \f$ n \f$.
*
*/
TYPED_TEST(KZGTest, SingleInLagrangeBasis)
{
const size_t n = 4;

using KZG = KZG<TypeParam>;
using Fr = typename TypeParam::ScalarField;

// create a random univariate (coefficients are in Lagrange basis)
auto witness = bb::Univariate<Fr, n>::get_random();
// define the interpolation domain
std::array<Fr, 4> eval_points = { Fr(0), Fr(1), Fr(2), Fr(3) };
// compute the monomial coefficients
Polynomial<Fr> witness_polynomial(std::span<Fr>(eval_points), std::span<Fr>(witness), n);
// commit to the polynomial in the monomial form
g1::element commitment = this->commit(witness_polynomial);

auto challenge = Fr::random_element();
// evaluate the original univariate
auto evaluation = witness.evaluate(challenge);
auto opening_pair = OpeningPair<TypeParam>{ challenge, evaluation };
auto opening_claim = OpeningClaim<TypeParam>{ opening_pair, commitment };

auto prover_transcript = NativeTranscript::prover_init_empty();

KZG::compute_opening_proof(this->ck(), { witness_polynomial, opening_pair }, prover_transcript);

auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);
auto pairing_points = KZG::reduce_verify(opening_claim, verifier_transcript);

EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true);
}
/**
* @brief Test full PCS protocol: Gemini, Shplonk, KZG and pairing check
* @details Demonstrates the full PCS protocol as it is used in the construction and verification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ template <typename Curve> class ShpleminiProver_ {
using ShplonkProver = ShplonkProver_<Curve>;
using GeminiProver = GeminiProver_<Curve>;

template <typename Transcript>
template <typename Transcript, size_t LENGTH = 0>
static OpeningClaim prove(const FF circuit_size,
RefSpan<Polynomial> f_polynomials,
RefSpan<Polynomial> g_polynomials,
std::span<FF> multilinear_challenge,
const std::shared_ptr<CommitmentKey<Curve>>& commitment_key,
const std::shared_ptr<Transcript>& transcript,
RefSpan<Polynomial> concatenated_polynomials = {},
const std::vector<RefVector<Polynomial>>& groups_to_be_concatenated = {})
const std::vector<RefVector<Polynomial>>& groups_to_be_concatenated = {},
const std::vector<bb::Univariate<FF, LENGTH>>& libra_univariates = {},
const std::vector<FF>& libra_evaluations = {})
{
std::vector<OpeningClaim> opening_claims = GeminiProver::prove(circuit_size,
f_polynomials,
Expand All @@ -38,8 +40,19 @@ template <typename Curve> class ShpleminiProver_ {
transcript,
concatenated_polynomials,
groups_to_be_concatenated);

OpeningClaim batched_claim = ShplonkProver::prove(commitment_key, opening_claims, transcript);
// Create opening claims for Libra masking univariates
std::vector<OpeningClaim> libra_opening_claims;
size_t idx = 0;
for (auto [libra_univariate, libra_evaluation] : zip_view(libra_univariates, libra_evaluations)) {
OpeningClaim new_claim;
new_claim.polynomial = Polynomial(libra_univariate);
new_claim.opening_pair.challenge = multilinear_challenge[idx];
new_claim.opening_pair.evaluation = libra_evaluation;
libra_opening_claims.push_back(new_claim);
idx++;
}
OpeningClaim batched_claim =
ShplonkProver::prove(commitment_key, opening_claims, transcript, libra_opening_claims);
return batched_claim;
};
};
Expand Down Expand Up @@ -117,7 +130,9 @@ template <typename Curve> class ShpleminiVerifier_ {
const Commitment& g1_identity,
const std::shared_ptr<Transcript>& transcript,
const std::vector<RefVector<Commitment>>& concatenation_group_commitments = {},
RefSpan<Fr> concatenated_evaluations = {})
RefSpan<Fr> concatenated_evaluations = {},
RefSpan<Commitment> libra_univariate_commitments = {},
const std::vector<Fr>& libra_univariate_evaluations = {})

{

Expand Down Expand Up @@ -254,6 +269,18 @@ template <typename Curve> class ShpleminiVerifier_ {
commitments.emplace_back(g1_identity);
scalars.emplace_back(constant_term_accumulator);

// For ZK flavors, the sumcheck output contains the evaluations of Libra univariates that submitted to the
// ShpleminiVerifier, otherwise this argument is set to be empty
if (!libra_univariate_evaluations.empty()) {
add_zk_data(commitments,
scalars,
libra_univariate_commitments,
libra_univariate_evaluations,
multivariate_challenge,
shplonk_batching_challenge,
shplonk_evaluation_challenge);
}

return { commitments, scalars, shplonk_evaluation_challenge };
};
/**
Expand Down Expand Up @@ -439,5 +466,66 @@ template <typename Curve> class ShpleminiVerifier_ {
commitments.emplace_back(std::move(fold_commitments[j]));
}
}

/**
* @brief Add the opening data corresponding to Libra masking univariates to the batched opening claim
*
* @details After verifying ZK Sumcheck, the verifier has to validate the claims about the evaluations of Libra
* univariates used to mask Sumcheck round univariates. To minimize the overhead of such openings, we continue the
* Shplonk batching started in Gemini, i.e. we add new claims multiplied by a suitable power of the Shplonk batching
* challenge and re-use the evaluation challenge sampled to prove the evaluations of Gemini polynomials.
*
* @param commitments
* @param scalars
* @param libra_univariate_commitments
* @param libra_univariate_evaluations
* @param multivariate_challenge
* @param shplonk_batching_challenge
* @param shplonk_evaluation_challenge
*/
static void add_zk_data(std::vector<Commitment>& commitments,
std::vector<Fr>& scalars,
RefSpan<Commitment> libra_univariate_commitments,
const std::vector<Fr>& libra_univariate_evaluations,
const std::vector<Fr>& multivariate_challenge,
const Fr& shplonk_batching_challenge,
const Fr& shplonk_evaluation_challenge)

{
// compute current power of Shplonk batching challenge taking into account the const proof size
Fr shplonk_challenge_power = Fr{ 1 };
for (size_t j = 0; j < CONST_PROOF_SIZE_LOG_N + 2; ++j) {
shplonk_challenge_power *= shplonk_batching_challenge;
}

// need to keep track of the contribution to the constant term
Fr& constant_term = scalars.back();
// compute shplonk denominators and batch invert them
std::vector<Fr> denominators;
size_t num_libra_univariates = libra_univariate_commitments.size();

// compute Shplonk denominators and invert them
for (size_t idx = 0; idx < num_libra_univariates; idx++) {
if constexpr (Curve::is_stdlib_type) {
denominators.push_back(Fr(1) / (shplonk_evaluation_challenge - multivariate_challenge[idx]));
} else {
denominators.push_back(shplonk_evaluation_challenge - multivariate_challenge[idx]);
}
};
if constexpr (!Curve::is_stdlib_type) {
Fr::batch_invert(denominators);
}
// add Libra commitments to the vector of commitments; compute corresponding scalars and the correction to the
// constant term
for (const auto [libra_univariate_commitment, denominator, libra_univariate_evaluation] :
zip_view(libra_univariate_commitments, denominators, libra_univariate_evaluations)) {
commitments.push_back(std::move(libra_univariate_commitment));
Fr scaling_factor = denominator * shplonk_challenge_power;
scalars.push_back((-scaling_factor));
shplonk_challenge_power *= shplonk_batching_challenge;
// update the constant term of the Shplonk batched claim
constant_term += scaling_factor * libra_univariate_evaluation;
}
}
};
} // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../shplonk/shplonk.hpp"
#include "../utils/batch_mul_native.hpp"
#include "barretenberg/commitment_schemes/claim.hpp"
#include "barretenberg/commitment_schemes/ipa/ipa.hpp"
#include "barretenberg/ecc/curves/bn254/g1.hpp"

#include <gtest/gtest.h>
Expand Down Expand Up @@ -221,4 +222,123 @@ TYPED_TEST(ShpleminiTest, CorrectnessOfGeminiClaimBatching)

EXPECT_EQ(shplemini_result, expected_result);
}

/**
* @brief Libra masking univariates are used in sumcheck to prevent the leakage of witness data through the evaluations
* of round univariates. Here we test the opening of log_n Libra masking univariates batched with the opening of several
* prover polynomials and their shifts.
*
*/
TYPED_TEST(ShpleminiTest, ShpleminiWithMaskingLibraUnivariates)
{
using ShpleminiProver = ShpleminiProver_<TypeParam>;
using ShpleminiVerifier = ShpleminiVerifier_<TypeParam>;
using KZG = KZG<TypeParam>;
using IPA = IPA<TypeParam>;
using Fr = typename TypeParam::ScalarField;
using Commitment = typename TypeParam::AffineElement;
using Polynomial = typename bb::Polynomial<Fr>;

const size_t n = 16;
const size_t log_n = 4;
// In practice, the length of Libra univariates is equal to FLAVOR::BATCHED_RELATION_PARTIAL_LENGTH
const size_t LIBRA_UNIVARIATE_LENGTH = 12;

std::array<Fr, LIBRA_UNIVARIATE_LENGTH> interpolation_domain;
for (size_t idx = 0; idx < LIBRA_UNIVARIATE_LENGTH; idx++) {
interpolation_domain[idx] = Fr(idx);
}
// Generate multilinear polynomials, their commitments (genuine and mocked) and evaluations (genuine) at a
// random point.
auto mle_opening_point = this->random_evaluation_point(log_n); // sometimes denoted 'u'
auto poly1 = Polynomial::random(n);
auto poly2 = Polynomial::random(n, 1);
auto poly3 = Polynomial::random(n, 1);
auto poly4 = Polynomial::random(n);

std::vector<bb::Univariate<Fr, LIBRA_UNIVARIATE_LENGTH>> libra_univariates;
std::vector<Commitment> libra_commitments;
std::vector<Fr> libra_evaluations;
for (size_t idx = 0; idx < log_n; idx++) {
// generate random polynomial
Polynomial libra_polynomial = Polynomial::random(LIBRA_UNIVARIATE_LENGTH);
// create a univariate with the same coefficients (to store an array instead of a vector)
bb::Univariate<Fr, LIBRA_UNIVARIATE_LENGTH> libra_univariate;
for (size_t i = 0; i < LIBRA_UNIVARIATE_LENGTH; i++) {
libra_univariate.value_at(i) = libra_polynomial[i];
}
libra_univariates.push_back(libra_univariate);

// commit to libra polynomial and populate the vector of libra commitments
Commitment libra_commitment = this->commit(libra_polynomial);
libra_commitments.push_back(libra_commitment);

// evaluate current libra univariate at the corresponding challenge and store the value in libra evaluations
libra_evaluations.push_back(libra_polynomial.evaluate(mle_opening_point[idx]));
}

Commitment commitment1 = this->commit(poly1);
Commitment commitment2 = this->commit(poly2);
Commitment commitment3 = this->commit(poly3);
Commitment commitment4 = this->commit(poly4);
std::vector<Commitment> unshifted_commitments = { commitment1, commitment2, commitment3, commitment4 };
std::vector<Commitment> shifted_commitments = { commitment2, commitment3 };
auto eval1 = poly1.evaluate_mle(mle_opening_point);
auto eval2 = poly2.evaluate_mle(mle_opening_point);
auto eval3 = poly3.evaluate_mle(mle_opening_point);
auto eval4 = poly4.evaluate_mle(mle_opening_point);
auto eval2_shift = poly2.evaluate_mle(mle_opening_point, true);
auto eval3_shift = poly3.evaluate_mle(mle_opening_point, true);

// Collect multilinear evaluations for input to prover
// std::vector<Fr> multilinear_evaluations = { eval1, eval2, eval3, eval4, eval2_shift, eval3_shift };

auto prover_transcript = NativeTranscript::prover_init_empty();

// Run the full prover PCS protocol:
auto opening_claim = ShpleminiProver::prove(Fr{ n },
RefArray{ poly1, poly2, poly3, poly4 },
RefArray{ poly2, poly3 },
mle_opening_point,
this->ck(),
prover_transcript,
/* concatenated_polynomials = */ {},
/* groups_to_be_concatenated = */ {},
libra_univariates,
libra_evaluations);
if constexpr (std::is_same_v<TypeParam, curve::Grumpkin>) {
IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
} else {
KZG::compute_opening_proof(this->ck(), opening_claim, prover_transcript);
}

// Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation)

auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript);

// Gemini verifier output:
// - claim: d+1 commitments to Fold_{r}^(0), Fold_{-r}^(0), Fold^(l), d+1 evaluations a_0_pos, a_l, l = 0:d-1
auto batch_opening_claim =
ShpleminiVerifier::compute_batch_opening_claim(n,
RefVector(unshifted_commitments),
RefVector(shifted_commitments),
RefArray{ eval1, eval2, eval3, eval4 },
RefArray{ eval2_shift, eval3_shift },
mle_opening_point,
this->vk()->get_g1_identity(),
verifier_transcript,
/* concatenation_group_commitments = */ {},
/* concatenated_evaluations = */ {},
RefVector(libra_commitments),
libra_evaluations);

if constexpr (std::is_same_v<TypeParam, curve::Grumpkin>) {
auto result = IPA::reduce_verify_batch_opening_claim(batch_opening_claim, this->vk(), verifier_transcript);
EXPECT_EQ(result, true);
} else {
const auto pairing_points = KZG::reduce_verify_batch_opening_claim(batch_opening_claim, verifier_transcript);
// Final pairing check: e([Q] - [Q_z] + z[W], [1]_2) = e([W], [x]_2)
EXPECT_EQ(this->vk()->pairing_check(pairing_points[0], pairing_points[1]), true);
}
}
} // namespace bb
Loading
Loading