Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
shplemini.test.cpp
Go to the documentation of this file.
12#include <gtest/gtest.h>
13
14using namespace bb;
15
16template <class PCS> class ShpleminiRecursionTest : public CommitmentTest<typename PCS::Curve::NativeCurve> {
17 public:
18 using Builder = typename PCS::Builder;
19 using Curve = typename PCS::Curve;
20 using NativeCurve = typename Curve::NativeCurve;
22 using NativePCS = typename PCS::PCSImpl;
23 using CommitmentKey = typename NativePCS::CK;
26 using Fr = typename Curve::ScalarField;
34
35 static constexpr size_t log_circuit_size = 10;
36
42
43 static std::vector<Fr> convert_elements_to_witnesses(Builder& builder, const std::vector<NativeFr>& elements)
44 {
45 std::vector<Fr> out(elements.size());
46 std::transform(elements.begin(), elements.end(), out.begin(), [&builder](const NativeFr& e) {
47 return Fr::from_witness(&builder, e);
48 });
49 return out;
50 }
51
52 static std::vector<Commitment> convert_commitments_to_witnesses(
53 Builder& builder, const std::vector<NativeCommitment>& native_commitments)
54 {
55 std::vector<Commitment> out(native_commitments.size());
56 std::transform(native_commitments.begin(),
57 native_commitments.end(),
58 out.begin(),
59 [&builder](const NativeCommitment& c) { return Commitment::from_witness(&builder, c); });
60 return out;
61 }
62
64 {
66 out.reserve(size);
67 for (size_t i = 0; i < size; ++i) {
68 out.emplace_back(NativeFr::random_element());
69 }
70 return out;
71 }
72
73 void validate_num_eccvm_rows(size_t num_polys, size_t num_shifted, bool short_scalars, Builder* builder)
74 {
75 size_t total_num_msm_rows = builder->op_queue->get_num_rows();
76
77 BB_ASSERT_LTE(total_num_msm_rows, 1UL << CONST_ECCVM_LOG_N, "ECCVM/Goblin: Too many ops in ecc op queue");
78
79 if (short_scalars) {
80
81 const size_t num_non_trivial_scalars_gemini_and_shplonk =
82 /* shifted + unshifted */ 2 + /* fold comms */ (log_circuit_size - 1) +
83 /* identity comm */ 1 + /*kzg comm */ 1;
84
85 const size_t num_msm_rows_unshifted = 33 * ((num_polys - 1 + 3) / 4) + 31;
86 const size_t num_msm_rows_shifted = 33 * ((num_shifted + 3) / 4) + 31;
87
88 EXPECT_EQ(total_num_msm_rows - num_msm_rows_shifted - num_msm_rows_unshifted,
89 33 * ((num_non_trivial_scalars_gemini_and_shplonk + 1) / 2) + 33);
90 } else {
91 const size_t num_non_trivial_scalars_gemini_and_shplonk =
92 /* fold comms */ (log_circuit_size - 1) +
93 /* identity comm */ 1 + /*kzg comm */ 1;
94
95 EXPECT_EQ(33 * ((num_polys + num_shifted + num_non_trivial_scalars_gemini_and_shplonk + 1) / 2) + 33,
96 total_num_msm_rows);
97 }
98 }
99
100 void run_shplemini_full_scalars(size_t num_polys, size_t num_shifted, bool prove_eccvm = false)
101 {
102 run_shplemini_generic(num_polys, num_shifted, false, prove_eccvm);
103 }
104 void run_shplemini_short_scalars(size_t num_polys, size_t num_shifted, bool prove_eccvm = false)
105 {
106 run_shplemini_generic(num_polys, num_shifted, true, prove_eccvm);
107 }
108
109 private:
110 void run_shplemini_generic(size_t num_polys, size_t num_shifted, bool short_scalars, bool prove_eccvm)
111 {
112 size_t N = 1 << log_circuit_size;
113
114 const auto padding_indicator_array =
115 stdlib::compute_padding_indicator_array<Curve, log_circuit_size>(log_circuit_size);
116
119
120 MockClaimGen mock_claims(N, num_polys, num_shifted, u_challenge, commitment_key);
121 auto prover_transcript = NativeTranscript::prover_init_empty();
122 // Initialize polys outside of `if` as they are used inside RefVector ClaimBatcher members.
123 Polynomial<NativeFr> squashed_unshifted(N);
125 // Labels are shared by Prover and Verifier
126 std::vector<std::string> unshifted_batching_challenge_labels;
127 std::vector<std::string> shifted_batching_challenge_labels;
128
129 for (size_t idx = 0; idx < num_polys - 1; idx++) {
130 unshifted_batching_challenge_labels.push_back("rho_" + std::to_string(idx));
131 }
132 for (size_t idx = 0; idx < num_shifted; idx++) {
133 shifted_batching_challenge_labels.push_back("rho_" + std::to_string(num_polys - 1 + idx));
134 }
135
136 if (short_scalars) {
137
138 auto unshifted_challenges =
139 prover_transcript->template get_challenges<NativeFr>(unshifted_batching_challenge_labels);
140 auto shifted_challenges =
141 prover_transcript->template get_challenges<NativeFr>(shifted_batching_challenge_labels);
142
143 squashed_unshifted += mock_claims.polynomial_batcher.unshifted[0];
144 for (size_t i = 0; i < unshifted_challenges.size(); ++i) {
145 squashed_unshifted.add_scaled(mock_claims.polynomial_batcher.unshifted[i + 1], unshifted_challenges[i]);
146 }
147
148 for (size_t i = 0; i < shifted_challenges.size(); ++i) {
149 squashed_shifted.add_scaled(mock_claims.polynomial_batcher.to_be_shifted_by_one[i],
150 shifted_challenges[i]);
151 }
152
153 mock_claims.polynomial_batcher.unshifted = RefVector{ squashed_unshifted };
154 mock_claims.polynomial_batcher.to_be_shifted_by_one = RefVector{ squashed_shifted };
155 }
156
157 auto prover_opening_claims =
158 ShpleminiProver::prove(N, mock_claims.polynomial_batcher, u_challenge, commitment_key, prover_transcript);
159
160 KZG<NativeCurve>::compute_opening_proof(commitment_key, prover_opening_claims, prover_transcript);
161
163 StdlibProof stdlib_proof(builder, prover_transcript->export_proof());
164 auto stdlib_verifier_transcript = std::make_shared<Transcript>(stdlib_proof);
165 [[maybe_unused]] auto _ = stdlib_verifier_transcript->template receive_from_prover<Fr>("Init");
166
167 // Execute Verifier protocol without the need for vk prior the final check
168 auto stdlib_unshifted_commitments =
170 auto stdlib_shifted_commitments =
172
173 auto stdlib_unshifted_evaluations =
175 auto stdlib_shifted_evaluations =
177
178 // Removing the free witness tag, since in the full scheme these are supposed to
179 // be fiat-shamirred earlier from the transcript
180 for (auto& comm : stdlib_unshifted_commitments) {
181 comm.unset_free_witness_tag();
182 }
183 for (auto& comm : stdlib_shifted_commitments) {
184 comm.unset_free_witness_tag();
185 }
186 for (auto& eval : stdlib_unshifted_evaluations) {
187 eval.unset_free_witness_tag();
188 }
189 for (auto& eval : stdlib_shifted_evaluations) {
190 eval.unset_free_witness_tag();
191 }
192
193 std::vector<Fr> u_challenge_in_circuit = convert_elements_to_witnesses(builder, u_challenge);
194 // Removing the free witness tag, since the u_challenge in the full scheme are supposed to
195 // be derived from the transcript earlier
196 for (auto& challenge : u_challenge_in_circuit) {
197 challenge.unset_free_witness_tag();
198 }
199
200 ClaimBatcher claim_batcher{
201 .unshifted = ClaimBatch{ RefVector(stdlib_unshifted_commitments), RefVector(stdlib_unshifted_evaluations) },
202 .shifted = ClaimBatch{ RefVector(stdlib_shifted_commitments), RefVector(stdlib_shifted_evaluations) }
203 };
204
205 if (short_scalars) {
206
207 // Helper that produces the vectors of batching challenges that will be fed to `batch_mul`
208 auto get_batching_challenges = [&](std::span<const std::string> unshifted_labels,
209 std::span<const std::string> shifted_labels) {
210 // `batch_mul` expects a vector of scalars.
211 std::vector<Fr> unshifted_challenges(num_polys);
212 // Except for the first commitment, each one of them is multiplied by a challenge, which is <= 128
213 // bits.
214 unshifted_challenges[0] = Fr(1);
215 // Get `num_polys - 1` short challenges to batch all unshifted commitments
216 auto tail = stdlib_verifier_transcript->template get_challenges<Fr>(unshifted_labels);
217 std::copy(tail.begin(), tail.end(), unshifted_challenges.begin() + 1);
218
219 // Get `num_shifted` short challenges to batch all shifted commitments
220 auto shifted_challenges = stdlib_verifier_transcript->template get_challenges<Fr>(shifted_labels);
221
222 return std::pair{ std::move(unshifted_challenges), std::move(shifted_challenges) };
223 };
224
225 auto [unshifted_challenges, shifted_challenges] =
226 get_batching_challenges(unshifted_batching_challenge_labels, shifted_batching_challenge_labels);
227
229 Commitment::batch_mul(claim_batcher.get_unshifted().commitments, unshifted_challenges, 128);
230
232 Commitment::batch_mul(claim_batcher.get_shifted().commitments, shifted_challenges, 128);
233
234 // Compute claimed evaluations
235 squashed_unshifted_eval = std::inner_product(unshifted_challenges.begin(),
236 unshifted_challenges.end(),
237 claim_batcher.get_unshifted().evaluations.begin(),
238 Fr(0));
239 squashed_shifted_eval = std::inner_product(shifted_challenges.begin(),
240 shifted_challenges.end(),
241 claim_batcher.get_shifted().evaluations.begin(),
242 Fr(0));
243
247 };
248 } else {
249 squashed_claim_batcher = claim_batcher;
250 }
251
252 auto opening_claim = ShpleminiVerifier::compute_batch_opening_claim(padding_indicator_array,
254 u_challenge_in_circuit,
255 Commitment::one(&builder),
256 stdlib_verifier_transcript);
258 KZG<Curve>::reduce_verify_batch_opening_claim(std::move(opening_claim), stdlib_verifier_transcript));
259 EXPECT_TRUE(CircuitChecker::check(builder));
260
262 EXPECT_EQ(vk.pairing_check(pairing_points.P0.get_value(), pairing_points.P1.get_value()), true);
263
265 validate_num_eccvm_rows(num_polys, num_shifted, short_scalars, &builder);
266 if (prove_eccvm) {
267 // Add hiding op for ECCVM ZK (prepended to ECCVM ops at row 1)
269 builder.queue_ecc_hiding_op(Fq::random_element(), Fq::random_element());
270 builder.op_queue->merge();
271 using clock = std::chrono::steady_clock;
272 auto start_total = clock::now();
273
275
276 auto start_builder = clock::now();
277 ECCVMCircuitBuilder eccvm_builder(builder.op_queue);
278
279 ECCVMProver prover(eccvm_builder, eccvm_transcript);
280
281 auto end_builder = clock::now();
282
283 info("ECCVM prover construction took ",
284 std::chrono::duration_cast<std::chrono::milliseconds>(end_builder - start_builder).count(),
285 " ms");
286
287 auto start_prove = clock::now();
288 prover.construct_proof();
289 auto end_prove = clock::now();
290
291 info("ECCVM proof construction took ",
292 std::chrono::duration_cast<std::chrono::milliseconds>(end_prove - start_prove).count(),
293 " ms");
294
295 auto end_total = clock::now();
296 info("ECCVM total (builder + proof) time: ",
297 std::chrono::duration_cast<std::chrono::milliseconds>(end_total - start_total).count(),
298 " ms");
299 }
300 } else {
301 info("builder num gates ", builder.get_num_finalized_gates_inefficient());
302 }
303 }
304};
305
311
317
320
321TEST_F(ShpleminiUltraTest, ProveAndVerifySingleFullScalars)
322{
323 // In UltraRecursiveVerifier instantiations, we remove the scalar muls against the commitments to shifts.
324 run_shplemini_full_scalars(bb::UltraFlavor::NUM_ALL_ENTITIES, 0);
325}
326
327TEST_F(ShpleminiUltraTest, ProveAndVerifySingleShortScalars)
328{
329 // We don't hit any edge cases here, because all commitments are random. Note that we are paying for the shifts
330 // here.
332}
333
334TEST_F(ShpleminiMegaTest, ProveAndVerifySingleFullScalars)
335{
336 size_t num_polys = 100;
337 size_t num_shifted = 5;
338
339 for (size_t idx = 0; idx < 4; idx++) {
340 run_shplemini_full_scalars(num_polys + idx, num_shifted + idx);
341 }
342}
343
344TEST_F(ShpleminiMegaTest, GoblinProveAndVerifyShortScalars)
345{
346 size_t num_polys = 100;
347 size_t num_shifted = 5;
348
349 for (size_t idx = 0; idx < 4; idx++) {
350 run_shplemini_short_scalars(num_polys + idx, num_shifted + idx);
351 }
352}
353
354TEST_F(ShpleminiMegaTest, ManyCommitmentsWithECCVMProving)
355{
356 run_shplemini_full_scalars(1700, 175, true);
357 run_shplemini_short_scalars(3500, 350, true);
358}
#define BB_ASSERT_LTE(left, right,...)
Definition assert.hpp:152
static std::vector< Fr > convert_elements_to_witnesses(Builder &builder, const std::vector< NativeFr > &elements)
typename PCS::Builder Builder
typename NativePCS::CK CommitmentKey
typename Curve::NativeCurve NativeCurve
void validate_num_eccvm_rows(size_t num_polys, size_t num_shifted, bool short_scalars, Builder *builder)
void run_shplemini_full_scalars(size_t num_polys, size_t num_shifted, bool prove_eccvm=false)
static std::vector< Commitment > convert_commitments_to_witnesses(Builder &builder, const std::vector< NativeCommitment > &native_commitments)
static std::vector< NativeFr > random_challenge_vector(size_t size)
void run_shplemini_short_scalars(size_t num_polys, size_t num_shifted, bool prove_eccvm=false)
typename NativeCurve::AffineElement NativeCommitment
typename ClaimBatcher::Batch ClaimBatch
typename PCS::Curve Curve
typename PCS::PCSImpl NativePCS
typename Curve::ScalarField Fr
typename NativeCurve::ScalarField NativeFr
static constexpr size_t log_circuit_size
ClaimBatcher squashed_claim_batcher
void run_shplemini_generic(size_t num_polys, size_t num_shifted, bool short_scalars, bool prove_eccvm)
typename Curve::AffineElement Commitment
Common transcript class for both parties. Stores the data for the current round, as well as the manif...
static std::shared_ptr< BaseTranscript > prover_init_empty()
For testing: initializes transcript with some arbitrary data so that a challenge can be generated aft...
std::pair< Proof, OpeningClaim > construct_proof()
RefVector< Polynomial > to_be_shifted_by_one
Definition gemini.hpp:139
RefVector< Polynomial > unshifted
Definition gemini.hpp:138
static void compute_opening_proof(const CK &ck, const ProverOpeningClaim< Curve > &opening_claim, const std::shared_ptr< Transcript > &prover_trancript)
Computes the KZG commitment to an opening proof polynomial at a single evaluation point.
Definition kzg.hpp:43
Structured polynomial class that represents the coefficients 'a' of a_0 + a_1 x .....
void add_scaled(PolynomialSpan< const Fr > other, Fr scaling_factor) &
adds the polynomial q(X) 'other', multiplied by a scaling factor.
A template class for a reference vector. Behaves as if std::vector<T&> was possible.
static OpeningClaim prove(const FF circuit_size, PolynomialBatcher &polynomial_batcher, std::span< FF > multilinear_challenge, const CommitmentKey< Curve > &commitment_key, const std::shared_ptr< Transcript > &transcript, const std::array< Polynomial, NUM_SMALL_IPA_EVALUATIONS > &libra_polynomials={}, const std::vector< Polynomial > &sumcheck_round_univariates={}, const std::vector< std::array< FF, 3 > > &sumcheck_round_evaluations={})
Definition shplemini.hpp:36
An efficient verifier for the evaluation proofs of multilinear polynomials and their shifts.
static BatchOpeningClaim< Curve > compute_batch_opening_claim(std::span< const Fr > padding_indicator_array, ClaimBatcher &claim_batcher, const std::vector< Fr > &multivariate_challenge, const Commitment &g1_identity, const std::shared_ptr< Transcript > &transcript, const RepeatedCommitmentsData &repeated_commitments={}, const bool has_zk=false, bool *consistency_checked=nullptr, const std::array< Commitment, NUM_LIBRA_COMMITMENTS > &libra_commitments={}, const Fr &libra_univariate_evaluation=Fr{ 0 }, const std::vector< Commitment > &sumcheck_round_commitments={}, const std::vector< std::array< Fr, 3 > > &sumcheck_round_evaluations={})
Non-padding version of compute_batch_opening_claim. Used by all native verifiers and by recursive Tra...
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
static constexpr size_t NUM_ALL_ENTITIES
static constexpr size_t NUM_WITNESS_ENTITIES
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
typename Group::affine_element AffineElement
Definition bn254.hpp:22
bb::fr ScalarField
Definition bn254.hpp:18
typename Group::affine_element AffineElement
Definition grumpkin.hpp:63
A simple wrapper around a vector of stdlib field elements representing a proof.
Definition proof.hpp:19
void info(Args... args)
Definition log.hpp:75
AluTraceBuilder builder
Definition alu.test.cpp:124
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:185
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
MegaCircuitBuilder_< field< Bn254FrParams > > MegaCircuitBuilder
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
For a small integer N = virtual_log_n and a given witness x = log_n, compute in-circuit an indicator_...
RefVector< Commitment > commitments
RefVector< Fr > evaluations
Logic to support batching opening claims for unshifted and shifted polynomials in Shplemini.
std::optional< Batch > unshifted
Constructs random polynomials, computes commitments and corresponding evaluations.
static field random_element(numeric::RNG *engine=nullptr) noexcept
An object storing two EC points that represent the inputs to a pairing check.