1use super::Certificate;
17use crate::{
18 AlgebraicSponge,
19 SNARK,
20 SNARKError,
21 fft::EvaluationDomain,
22 polycommit::sonic_pc::{
23 Commitment,
24 CommitterUnionKey,
25 Evaluations,
26 LabeledCommitment,
27 QuerySet,
28 Randomness,
29 SonicKZG10,
30 },
31 r1cs::{ConstraintSynthesizer, SynthesisError},
32 snark::varuna::{
33 CircuitProvingKey,
34 CircuitVerifyingKey,
35 Proof,
36 SNARKMode,
37 UniversalSRS,
38 ahp::{AHPError, AHPForR1CS, CircuitId, EvaluationsProvider},
39 proof,
40 prover,
41 witness_label,
42 },
43 srs::UniversalVerifier,
44};
45use rand::RngCore;
46use snarkvm_curves::PairingEngine;
47use snarkvm_fields::{One, PrimeField, ToConstraintField, Zero};
48use snarkvm_utilities::{ToBytes, to_bytes_le};
49
50use anyhow::{Result, anyhow, bail, ensure};
51use core::marker::PhantomData;
52use itertools::Itertools;
53use rand::{CryptoRng, Rng};
54use std::{borrow::Borrow, collections::BTreeMap, ops::Deref, sync::Arc};
55
56use crate::srs::UniversalProver;
57use snarkvm_utilities::println;
58
59#[derive(Clone, Debug)]
61pub struct VarunaSNARK<E: PairingEngine, FS: AlgebraicSponge<E::Fq, 2>, SM: SNARKMode>(
62 #[doc(hidden)] PhantomData<(E, FS, SM)>,
63);
64
65impl<E: PairingEngine, FS: AlgebraicSponge<E::Fq, 2>, SM: SNARKMode> VarunaSNARK<E, FS, SM> {
66 pub const PROTOCOL_NAME: &'static [u8] = b"VARUNA-2023";
69
70 pub fn batch_circuit_setup<C: ConstraintSynthesizer<E::Fr>>(
73 universal_srs: &UniversalSRS<E>,
74 circuits: &[&C],
75 ) -> Result<Vec<(CircuitProvingKey<E, SM>, CircuitVerifyingKey<E>)>> {
76 let index_time = start_timer!(|| "Varuna::CircuitSetup");
77
78 let universal_prover = &universal_srs.to_universal_prover()?;
79
80 let mut circuit_keys = Vec::with_capacity(circuits.len());
81 for circuit in circuits {
82 let mut indexed_circuit = AHPForR1CS::<_, SM>::index(*circuit)?;
83 universal_srs.download_powers_for(0..indexed_circuit.max_degree()?).map_err(|e| {
86 anyhow!("Failed to download powers for degree {}: {e}", indexed_circuit.max_degree().unwrap())
87 })?;
88 let coefficient_support = AHPForR1CS::<E::Fr, SM>::get_degree_bounds(&indexed_circuit.index_info)?;
89
90 let supported_hiding_bound = 1;
92 let supported_lagrange_sizes = [].into_iter(); let (committer_key, _) = SonicKZG10::<E, FS>::trim(
94 universal_srs,
95 indexed_circuit.max_degree()?,
96 supported_lagrange_sizes,
97 supported_hiding_bound,
98 Some(coefficient_support.as_slice()),
99 )?;
100
101 let ck = CommitterUnionKey::union(std::iter::once(&committer_key));
102
103 let commit_time = start_timer!(|| format!("Commit to index polynomials for {}", indexed_circuit.id));
104 let setup_rng = None::<&mut dyn RngCore>; let (mut circuit_commitments, commitment_randomnesses): (_, _) = SonicKZG10::<E, FS>::commit(
107 universal_prover,
108 &ck,
109 indexed_circuit.interpolate_matrix_evals()?.map(Into::into),
110 setup_rng,
111 )?;
112 let empty_randomness = Randomness::<E>::empty();
113 ensure!(commitment_randomnesses.iter().all(|r| r == &empty_randomness));
114 end_timer!(commit_time);
115
116 circuit_commitments.sort_by(|c1, c2| c1.label().cmp(c2.label()));
117 let circuit_commitments = circuit_commitments.into_iter().map(|c| *c.commitment()).collect();
118 indexed_circuit.prune_row_col_evals();
119 let circuit_verifying_key = CircuitVerifyingKey {
120 circuit_info: indexed_circuit.index_info,
121 circuit_commitments,
122 id: indexed_circuit.id,
123 };
124 let circuit_proving_key = CircuitProvingKey {
125 circuit_verifying_key: circuit_verifying_key.clone(),
126 circuit: Arc::new(indexed_circuit),
127 committer_key: Arc::new(committer_key),
128 };
129 circuit_keys.push((circuit_proving_key, circuit_verifying_key));
130 }
131
132 end_timer!(index_time);
133 Ok(circuit_keys)
134 }
135
136 fn init_sponge<'a>(
137 fs_parameters: &FS::Parameters,
138 inputs_and_batch_sizes: &BTreeMap<CircuitId, (usize, &[Vec<E::Fr>])>,
139 circuit_commitments: impl Iterator<Item = &'a [crate::polycommit::sonic_pc::Commitment<E>]>,
140 ) -> FS {
141 let mut sponge = FS::new_with_parameters(fs_parameters);
142 sponge.absorb_bytes(Self::PROTOCOL_NAME);
143 for (batch_size, inputs) in inputs_and_batch_sizes.values() {
144 sponge.absorb_bytes(&(*batch_size as u64).to_le_bytes());
145 for input in inputs.iter() {
146 sponge.absorb_nonnative_field_elements(input.iter().copied());
147 }
148 }
149 for circuit_specific_commitments in circuit_commitments {
150 sponge.absorb_native_field_elements(circuit_specific_commitments);
151 }
152 sponge
153 }
154
155 fn init_sponge_for_certificate(
156 fs_parameters: &FS::Parameters,
157 verifying_key: &CircuitVerifyingKey<E>,
158 ) -> Result<FS> {
159 let mut sponge = FS::new_with_parameters(fs_parameters);
160 sponge.absorb_bytes(&to_bytes_le![&Self::PROTOCOL_NAME]?);
161 sponge.absorb_bytes(&verifying_key.circuit_info.to_bytes_le()?);
162 sponge.absorb_native_field_elements(&verifying_key.circuit_commitments);
163 sponge.absorb_bytes(&verifying_key.id.0);
164 Ok(sponge)
165 }
166
167 fn absorb_labeled_with_sums(
168 comms: &[LabeledCommitment<Commitment<E>>],
169 sums: &[prover::MatrixSums<E::Fr>],
170 sponge: &mut FS,
171 ) {
172 let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
173 Self::absorb_with_sums(&commitments, sums, sponge)
174 }
175
176 fn absorb_labeled(comms: &[LabeledCommitment<Commitment<E>>], sponge: &mut FS) {
177 let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
178 Self::absorb(&commitments, sponge);
179 }
180
181 fn absorb(commitments: &[Commitment<E>], sponge: &mut FS) {
182 let sponge_time = start_timer!(|| "Absorbing commitments");
183 sponge.absorb_native_field_elements(commitments);
184 end_timer!(sponge_time);
185 }
186
187 fn absorb_with_sums(commitments: &[Commitment<E>], sums: &[prover::MatrixSums<E::Fr>], sponge: &mut FS) {
188 let sponge_time = start_timer!(|| "Absorbing commitments and message");
189 Self::absorb(commitments, sponge);
190 for sum in sums.iter() {
191 sponge.absorb_nonnative_field_elements([sum.sum_a, sum.sum_b, sum.sum_c]);
192 }
193 end_timer!(sponge_time);
194 }
195}
196
197impl<E: PairingEngine, FS, SM> SNARK for VarunaSNARK<E, FS, SM>
198where
199 E::Fr: PrimeField,
200 E::Fq: PrimeField,
201 FS: AlgebraicSponge<E::Fq, 2>,
202 SM: SNARKMode,
203{
204 type BaseField = E::Fq;
205 type Certificate = Certificate<E>;
206 type FSParameters = FS::Parameters;
207 type FiatShamirRng = FS;
208 type Proof = Proof<E>;
209 type ProvingKey = CircuitProvingKey<E, SM>;
210 type ScalarField = E::Fr;
211 type UniversalProver = UniversalProver<E>;
212 type UniversalSRS = UniversalSRS<E>;
213 type UniversalVerifier = UniversalVerifier<E>;
214 type VerifierInput = [E::Fr];
215 type VerifyingKey = CircuitVerifyingKey<E>;
216
217 fn universal_setup(max_degree: usize) -> Result<Self::UniversalSRS> {
218 let setup_time = start_timer!(|| { format!("Varuna::UniversalSetup with max_degree {max_degree}",) });
219 let srs = SonicKZG10::<E, FS>::load_srs(max_degree).map_err(Into::into);
220 end_timer!(setup_time);
221 srs
222 }
223
224 fn circuit_setup<C: ConstraintSynthesizer<E::Fr>>(
227 universal_srs: &Self::UniversalSRS,
228 circuit: &C,
229 ) -> Result<(Self::ProvingKey, Self::VerifyingKey)> {
230 let mut circuit_keys = Self::batch_circuit_setup::<C>(universal_srs, &[circuit])?;
231 ensure!(circuit_keys.len() == 1);
232 Ok(circuit_keys.pop().unwrap())
233 }
234
235 fn prove_vk(
237 universal_prover: &Self::UniversalProver,
238 fs_parameters: &Self::FSParameters,
239 verifying_key: &Self::VerifyingKey,
240 proving_key: &Self::ProvingKey,
241 ) -> Result<Self::Certificate> {
242 let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
244 let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
248 let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
249 let one = E::Fr::one();
250 let linear_combination_challenges = core::iter::once(&one).chain(challenges.iter());
251
252 let circuit_id = std::iter::once(&verifying_key.id);
253 let circuit_poly_info = AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_id);
254
255 let mut lc = crate::polycommit::sonic_pc::LinearCombination::empty("circuit_check");
257 for (label, &c) in circuit_poly_info.keys().zip(linear_combination_challenges) {
258 lc.add(c, label.clone());
259 }
260
261 let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
262 let committer_key = CommitterUnionKey::union(std::iter::once(proving_key.committer_key.as_ref()));
263
264 let empty_randomness = vec![Randomness::<E>::empty(); 12];
265 let certificate = SonicKZG10::<E, FS>::open_combinations(
266 universal_prover,
267 &committer_key,
268 &[lc],
269 proving_key.circuit.interpolate_matrix_evals()?,
270 &empty_randomness,
271 &query_set,
272 &mut sponge,
273 )?;
274
275 Ok(Self::Certificate::new(certificate))
276 }
277
278 fn verify_vk<C: ConstraintSynthesizer<Self::ScalarField>>(
281 universal_verifier: &Self::UniversalVerifier,
282 fs_parameters: &Self::FSParameters,
283 circuit: &C,
284 verifying_key: &Self::VerifyingKey,
285 certificate: &Self::Certificate,
286 ) -> Result<bool> {
287 let circuit_id = &verifying_key.id;
289 let state = AHPForR1CS::<E::Fr, SM>::index_helper(circuit)?;
290 if state.index_info != verifying_key.circuit_info {
291 bail!(SNARKError::CircuitNotFound);
292 }
293 if state.id != *circuit_id {
294 bail!(SNARKError::CircuitNotFound);
295 }
296
297 let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
299
300 let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
304 let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
305 let combiners = core::iter::once(E::Fr::one()).chain(challenges);
306
307 let (lc, evaluation) =
309 AHPForR1CS::<E::Fr, SM>::evaluate_index_polynomials(state, circuit_id, point, combiners)?;
310
311 ensure!(verifying_key.circuit_commitments.len() == lc.terms.len());
312 let commitments = verifying_key
313 .iter()
314 .cloned()
315 .zip_eq(lc.terms.keys())
316 .map(|(c, label)| LabeledCommitment::new(format!("{label:?}"), c, None))
317 .collect_vec();
318 let evaluations = Evaluations::from_iter([(("circuit_check".into(), point), evaluation)]);
319 let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
320
321 SonicKZG10::<E, FS>::check_combinations(
322 universal_verifier,
323 &[lc],
324 &commitments,
325 &query_set,
326 &evaluations,
327 &certificate.pc_proof,
328 &mut sponge,
329 )
330 .map_err(Into::into)
331 }
332
333 fn prove_batch<C: ConstraintSynthesizer<E::Fr>, R: Rng + CryptoRng>(
337 universal_prover: &Self::UniversalProver,
338 fs_parameters: &Self::FSParameters,
339 keys_to_constraints: &BTreeMap<&CircuitProvingKey<E, SM>, &[C]>,
340 zk_rng: &mut R,
341 ) -> Result<Self::Proof> {
342 let prover_time = start_timer!(|| "Varuna::Prover");
343 if keys_to_constraints.is_empty() {
344 bail!(SNARKError::EmptyBatch);
345 }
346
347 let mut circuits_to_constraints = BTreeMap::new();
348 for (pk, constraints) in keys_to_constraints {
349 circuits_to_constraints.insert(pk.circuit.deref(), *constraints);
350 }
351 let prover_state = AHPForR1CS::<_, SM>::init_prover(&circuits_to_constraints, zk_rng)?;
352
353 let mut batch_sizes = BTreeMap::new();
355 let mut circuit_infos = BTreeMap::new();
356 let mut inputs_and_batch_sizes = BTreeMap::new();
357 let mut total_instances = 0usize;
358 let mut public_inputs = BTreeMap::new(); let num_unique_circuits = keys_to_constraints.len();
360 let mut circuit_ids = Vec::with_capacity(num_unique_circuits);
361 for pk in keys_to_constraints.keys() {
362 let batch_size = prover_state.batch_size(&pk.circuit).ok_or(SNARKError::CircuitNotFound)?;
363 let public_input = prover_state.public_inputs(&pk.circuit).ok_or(SNARKError::CircuitNotFound)?;
364 let padded_public_input =
365 prover_state.padded_public_inputs(&pk.circuit).ok_or(SNARKError::CircuitNotFound)?;
366 let circuit_id = pk.circuit.id;
367 batch_sizes.insert(circuit_id, batch_size);
368 circuit_infos.insert(circuit_id, &pk.circuit_verifying_key.circuit_info);
369 inputs_and_batch_sizes.insert(circuit_id, (batch_size, padded_public_input));
370 public_inputs.insert(circuit_id, public_input);
371 total_instances = total_instances.saturating_add(batch_size);
372
373 circuit_ids.push(circuit_id);
374 }
375 ensure!(prover_state.total_instances == total_instances);
376
377 let committer_key = CommitterUnionKey::union(keys_to_constraints.keys().map(|pk| pk.committer_key.deref()));
378
379 let circuit_commitments =
380 keys_to_constraints.keys().map(|pk| pk.circuit_verifying_key.circuit_commitments.as_slice());
381
382 let mut sponge = Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone());
383
384 let prover_state = AHPForR1CS::<_, SM>::prover_first_round(prover_state, zk_rng)?;
388
389 let first_round_comm_time = start_timer!(|| "Committing to first round polys");
390 let (first_commitments, first_commitment_randomnesses) = {
391 let first_round_oracles = prover_state.first_round_oracles.as_ref().unwrap();
392 SonicKZG10::<E, FS>::commit(
393 universal_prover,
394 &committer_key,
395 first_round_oracles.iter().map(Into::into),
396 SM::ZK.then_some(zk_rng),
397 )?
398 };
399 end_timer!(first_round_comm_time);
400
401 Self::absorb_labeled(&first_commitments, &mut sponge);
402
403 let (verifier_first_message, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
404 &batch_sizes,
405 &circuit_infos,
406 prover_state.max_constraint_domain,
407 prover_state.max_variable_domain,
408 prover_state.max_non_zero_domain,
409 &mut sponge,
410 )?;
411 let (second_oracles, prover_state) =
417 AHPForR1CS::<_, SM>::prover_second_round(&verifier_first_message, prover_state, zk_rng)?;
418
419 let second_round_comm_time = start_timer!(|| "Committing to second round polys");
420 let (second_commitments, second_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
421 universal_prover,
422 &committer_key,
423 second_oracles.iter().map(Into::into),
424 SM::ZK.then_some(zk_rng),
425 )?;
426 end_timer!(second_round_comm_time);
427
428 Self::absorb_labeled(&second_commitments, &mut sponge);
429
430 let (verifier_second_msg, verifier_state) =
431 AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge)?;
432 let (prover_third_message, third_oracles, prover_state) = AHPForR1CS::<_, SM>::prover_third_round(
438 &verifier_first_message,
439 &verifier_second_msg,
440 prover_state,
441 zk_rng,
442 )?;
443
444 let third_round_comm_time = start_timer!(|| "Committing to third round polys");
445 let (third_commitments, third_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
446 universal_prover,
447 &committer_key,
448 third_oracles.iter().map(Into::into),
449 SM::ZK.then_some(zk_rng),
450 )?;
451 end_timer!(third_round_comm_time);
452
453 Self::absorb_labeled_with_sums(
454 &third_commitments,
455 &prover_third_message.sums.clone().into_iter().flatten().collect_vec(),
456 &mut sponge,
457 );
458
459 let (verifier_third_msg, verifier_state) =
460 AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
461 let (prover_fourth_message, fourth_oracles, mut prover_state) =
467 AHPForR1CS::<_, SM>::prover_fourth_round(&verifier_second_msg, &verifier_third_msg, prover_state, zk_rng)?;
468
469 let fourth_round_comm_time = start_timer!(|| "Committing to fourth round polys");
470 let (fourth_commitments, fourth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
471 universal_prover,
472 &committer_key,
473 fourth_oracles.iter().map(Into::into),
474 SM::ZK.then_some(zk_rng),
475 )?;
476 end_timer!(fourth_round_comm_time);
477
478 Self::absorb_labeled_with_sums(&fourth_commitments, &prover_fourth_message.sums, &mut sponge);
479
480 let (verifier_fourth_msg, verifier_state) =
481 AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
482 let first_round_oracles = prover_state.first_round_oracles.take().unwrap();
486 let index_a_polys =
487 prover_state.circuit_specific_states.values_mut().flat_map(|s| s.a_polys.take().unwrap()).collect_vec();
488 let index_b_polys =
489 prover_state.circuit_specific_states.values_mut().flat_map(|s| s.b_polys.take().unwrap()).collect_vec();
490
491 let fifth_oracles = AHPForR1CS::<_, SM>::prover_fifth_round(verifier_fourth_msg, prover_state, zk_rng)?;
494
495 let fifth_round_comm_time = start_timer!(|| "Committing to fifth round polys");
496 let (fifth_commitments, fifth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
497 universal_prover,
498 &committer_key,
499 fifth_oracles.iter().map(Into::into),
500 SM::ZK.then_some(zk_rng),
501 )?;
502 end_timer!(fifth_round_comm_time);
503
504 Self::absorb_labeled(&fifth_commitments, &mut sponge);
505
506 let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
507 let polynomials: Vec<_> = index_a_polys
511 .into_iter()
512 .chain(index_b_polys)
513 .chain(first_round_oracles.into_iter())
514 .chain(second_oracles.into_iter())
515 .chain(third_oracles.into_iter())
516 .chain(fourth_oracles.into_iter())
517 .chain(fifth_oracles.into_iter())
518 .collect();
519 ensure!(
520 polynomials.len()
521 == num_unique_circuits * 6 + AHPForR1CS::<E::Fr, SM>::num_first_round_oracles(total_instances) +
523 AHPForR1CS::<E::Fr, SM>::num_second_round_oracles() +
524 AHPForR1CS::<E::Fr, SM>::num_third_round_oracles() +
525 AHPForR1CS::<E::Fr, SM>::num_fourth_round_oracles(num_unique_circuits) +
526 AHPForR1CS::<E::Fr, SM>::num_fifth_round_oracles()
527 );
528
529 let witness_comm_len = if SM::ZK { first_commitments.len() - 1 } else { first_commitments.len() };
531 let mask_poly = SM::ZK.then(|| *first_commitments[witness_comm_len].commitment());
532 let witness_commitments = first_commitments[..witness_comm_len]
533 .iter()
534 .map(|c| proof::WitnessCommitments { w: *c.commitment() })
535 .collect_vec();
536 let fourth_commitments_chunked = fourth_commitments.chunks_exact(3);
537 let (g_a_commitments, g_b_commitments, g_c_commitments) = fourth_commitments_chunked
538 .map(|c| (*c[0].commitment(), *c[1].commitment(), *c[2].commitment()))
539 .multiunzip();
540
541 #[rustfmt::skip]
542 let commitments = proof::Commitments {
543 witness_commitments,
544 mask_poly,
545 h_0: *second_commitments[0].commitment(),
546 g_1: *third_commitments[0].commitment(),
547 h_1: *third_commitments[1].commitment(),
548 g_a_commitments,
549 g_b_commitments,
550 g_c_commitments,
551 h_2: *fifth_commitments[0].commitment(),
552 };
553
554 let indexer_randomness = vec![Randomness::<E>::empty(); 6 * num_unique_circuits];
556 let commitment_randomnesses: Vec<Randomness<E>> = indexer_randomness
557 .into_iter()
558 .chain(first_commitment_randomnesses)
559 .chain(second_commitment_randomnesses)
560 .chain(third_commitment_randomnesses)
561 .chain(fourth_commitment_randomnesses)
562 .chain(fifth_commitment_randomnesses)
563 .collect();
564
565 let empty_randomness = Randomness::<E>::empty();
566 if SM::ZK {
567 ensure!(commitment_randomnesses.iter().any(|r| r != &empty_randomness));
568 } else {
569 ensure!(commitment_randomnesses.iter().all(|r| r == &empty_randomness));
570 }
571
572 let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
574 let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
575 &public_inputs,
576 &polynomials,
577 &prover_third_message,
578 &prover_fourth_message,
579 &verifier_state,
580 )?;
581
582 let eval_time = start_timer!(|| "Evaluating linear combinations over query set");
583 let mut evaluations = std::collections::BTreeMap::new();
584 for (label, (_, point)) in query_set.to_set() {
585 if !AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_str()) {
586 let lc = lc_s.get(&label).ok_or_else(|| AHPError::MissingEval(label.to_string()))?;
587 let evaluation = polynomials.get_lc_eval(lc, point)?;
588 evaluations.insert(label, evaluation);
589 }
590 }
591
592 let evaluations = proof::Evaluations::from_map(&evaluations, batch_sizes.clone());
593 end_timer!(eval_time);
594
595 sponge.absorb_nonnative_field_elements(evaluations.to_field_elements());
596
597 let pc_proof = SonicKZG10::<E, FS>::open_combinations(
598 universal_prover,
599 &committer_key,
600 lc_s.values(),
601 polynomials,
602 &commitment_randomnesses,
603 &query_set.to_set(),
604 &mut sponge,
605 )?;
606
607 let proof = Proof::<E>::new(
608 batch_sizes,
609 commitments,
610 evaluations,
611 prover_third_message,
612 prover_fourth_message,
613 pc_proof,
614 )?;
615 proof.check_batch_sizes()?;
616 ensure!(proof.pc_proof.is_hiding() == SM::ZK);
617
618 end_timer!(prover_time);
619 Ok(proof)
620 }
621
622 fn verify_batch<B: Borrow<Self::VerifierInput>>(
626 universal_verifier: &Self::UniversalVerifier,
627 fs_parameters: &Self::FSParameters,
628 keys_to_inputs: &BTreeMap<&Self::VerifyingKey, &[B]>,
629 proof: &Self::Proof,
630 ) -> Result<bool> {
631 if keys_to_inputs.is_empty() {
632 bail!(SNARKError::EmptyBatch);
633 }
634
635 proof.check_batch_sizes()?;
636 let batch_sizes_vec = proof.batch_sizes();
637 let mut batch_sizes = BTreeMap::new();
638 for (i, (vk, public_inputs_i)) in keys_to_inputs.iter().enumerate() {
639 batch_sizes.insert(vk.id, batch_sizes_vec[i]);
640
641 if public_inputs_i.is_empty() {
642 bail!(SNARKError::EmptyBatch);
643 }
644
645 if public_inputs_i.len() != batch_sizes_vec[i] {
646 bail!(SNARKError::BatchSizeMismatch);
647 }
648 }
649
650 let mut max_num_constraints = 0;
652 let mut max_num_variables = 0;
653 let mut max_non_zero_domain = None;
654 let mut public_inputs = BTreeMap::new();
655 let mut padded_public_vec = Vec::with_capacity(keys_to_inputs.len());
656 let mut inputs_and_batch_sizes = BTreeMap::new();
657 let mut input_domains = BTreeMap::new();
658 let mut circuit_infos = BTreeMap::new();
659 let mut circuit_ids = Vec::with_capacity(keys_to_inputs.len());
660 for (&vk, &public_inputs_i) in keys_to_inputs.iter() {
661 max_num_constraints = max_num_constraints.max(vk.circuit_info.num_constraints);
662 max_num_variables = max_num_variables.max(vk.circuit_info.num_public_and_private_variables);
663
664 let non_zero_domains = AHPForR1CS::<_, SM>::cmp_non_zero_domains(&vk.circuit_info, max_non_zero_domain)?;
665 max_non_zero_domain = non_zero_domains.max_non_zero_domain;
666
667 let input_domain = EvaluationDomain::<E::Fr>::new(vk.circuit_info.num_public_inputs)
668 .ok_or(anyhow!("Failed to create EvaluationDomain from num_public_inputs"))?;
669 input_domains.insert(vk.id, input_domain);
670
671 let input_fields = public_inputs_i
672 .iter()
673 .map(|input| {
674 let input = input.borrow().to_field_elements()?;
675 ensure!(input.len() > 0);
676 ensure!(input[0] == E::Fr::one());
677 if input.len() > input_domain.size() {
678 bail!(SNARKError::PublicInputSizeMismatch);
679 }
680 Ok(input)
681 })
682 .collect::<Result<Vec<_>, _>>()?;
683
684 let (padded_public_inputs_i, parsed_public_inputs_i): (Vec<_>, Vec<_>) = {
685 input_fields
686 .iter()
687 .map(|input| {
688 let input_len = input.len().max(input_domain.size());
689 let mut new_input = Vec::with_capacity(input_len);
690 new_input.extend_from_slice(input);
691 new_input.resize(input_len, E::Fr::zero());
692 if cfg!(debug_assertions) {
693 println!("Number of padded public variables: {}", new_input.len());
694 }
695 let unformatted = prover::ConstraintSystem::unformat_public_input(&new_input);
696 (new_input, unformatted)
697 })
698 .unzip()
699 };
700 let circuit_id = vk.id;
701 public_inputs.insert(circuit_id, parsed_public_inputs_i);
702 padded_public_vec.push(padded_public_inputs_i);
703 circuit_infos.insert(circuit_id, &vk.circuit_info);
704 circuit_ids.push(circuit_id);
705 }
706 for (i, (vk, &batch_size)) in keys_to_inputs.keys().zip(batch_sizes.values()).enumerate() {
707 inputs_and_batch_sizes.insert(vk.id, (batch_size, padded_public_vec[i].as_slice()));
708 }
709 let max_constraint_domain =
710 EvaluationDomain::<E::Fr>::new(max_num_constraints).ok_or(SynthesisError::PolyTooLarge)?;
711 let max_variable_domain =
712 EvaluationDomain::<E::Fr>::new(max_num_variables).ok_or(SynthesisError::PolyTooLarge)?;
713 let max_non_zero_domain = max_non_zero_domain.ok_or(SynthesisError::PolyTooLarge)?;
714
715 let comms = &proof.commitments;
716 let proof_has_correct_zk_mode = if SM::ZK {
717 proof.pc_proof.is_hiding() & comms.mask_poly.is_some()
718 } else {
719 !proof.pc_proof.is_hiding() & comms.mask_poly.is_none()
720 };
721 if !proof_has_correct_zk_mode {
722 eprintln!(
723 "Found `mask_poly` in the first round when not expected, or proof has incorrect hiding mode ({})",
724 proof.pc_proof.is_hiding()
725 );
726 return Ok(false);
727 }
728
729 let verifier_time = start_timer!(|| format!("Varuna::Verify with batch sizes: {:?}", batch_sizes));
730
731 let first_round_info = AHPForR1CS::<E::Fr, SM>::first_round_polynomial_info(batch_sizes.iter());
732
733 let mut first_comms_consumed = 0;
734 let mut first_commitments = batch_sizes
735 .iter()
736 .flat_map(|(circuit_id, &batch_size)| {
737 let first_comms = comms.witness_commitments[first_comms_consumed..][..batch_size]
738 .iter()
739 .enumerate()
740 .map(|(j, w_comm)| {
741 LabeledCommitment::new_with_info(
742 &first_round_info[&witness_label(*circuit_id, "w", j)],
743 w_comm.w,
744 )
745 });
746 first_comms_consumed += batch_size;
747 first_comms
748 })
749 .collect_vec();
750
751 if SM::ZK {
752 first_commitments.push(LabeledCommitment::new_with_info(
753 first_round_info.get("mask_poly").ok_or(anyhow!("Missing mask_poly"))?,
754 comms.mask_poly.ok_or(anyhow!("Missing mask_poly"))?,
755 ));
756 }
757
758 let second_round_info = AHPForR1CS::<E::Fr, SM>::second_round_polynomial_info();
759 let second_commitments = [LabeledCommitment::new_with_info(&second_round_info["h_0"], comms.h_0)];
760
761 let third_round_info = AHPForR1CS::<E::Fr, SM>::third_round_polynomial_info(max_variable_domain.size());
762 let third_commitments = [
763 LabeledCommitment::new_with_info(&third_round_info["g_1"], comms.g_1),
764 LabeledCommitment::new_with_info(&third_round_info["h_1"], comms.h_1),
765 ];
766
767 let fourth_round_info =
768 AHPForR1CS::<E::Fr, SM>::fourth_round_polynomial_info(circuit_infos.clone().into_iter());
769 let fourth_commitments = comms
770 .g_a_commitments
771 .iter()
772 .zip_eq(comms.g_b_commitments.iter())
773 .zip_eq(comms.g_c_commitments.iter())
774 .zip_eq(circuit_ids.iter())
775 .flat_map(|(((g_a, g_b), g_c), circuit_id)| {
776 [
777 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_a", 0)], *g_a),
778 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_b", 0)], *g_b),
779 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_c", 0)], *g_c),
780 ]
781 })
782 .collect_vec();
783
784 let fifth_round_info = AHPForR1CS::<E::Fr, SM>::fifth_round_polynomial_info();
785 let fifth_commitments = [LabeledCommitment::new_with_info(&fifth_round_info["h_2"], comms.h_2)];
786
787 let circuit_commitments = keys_to_inputs.keys().map(|vk| vk.circuit_commitments.as_slice());
788 let mut sponge = Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone());
789
790 let first_round_time = start_timer!(|| "First round");
793 Self::absorb_labeled(&first_commitments, &mut sponge);
794 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
795 &batch_sizes,
796 &circuit_infos,
797 max_constraint_domain,
798 max_variable_domain,
799 max_non_zero_domain,
800 &mut sponge,
801 )?;
802 end_timer!(first_round_time);
803 let second_round_time = start_timer!(|| "Second round");
808 Self::absorb_labeled(&second_commitments, &mut sponge);
809 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge)?;
810 end_timer!(second_round_time);
811 let third_round_time = start_timer!(|| "Third round");
816 Self::absorb_labeled_with_sums(
817 &third_commitments,
818 &proof.third_msg.sums.clone().into_iter().flatten().collect_vec(),
819 &mut sponge,
820 );
821 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
822 end_timer!(third_round_time);
823 let fourth_round_time = start_timer!(|| "Fourth round");
828
829 Self::absorb_labeled_with_sums(&fourth_commitments, &proof.fourth_msg.sums, &mut sponge);
830 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
831 end_timer!(fourth_round_time);
832 let fifth_round_time = start_timer!(|| "Fifth round");
837
838 Self::absorb_labeled(&fifth_commitments, &mut sponge);
839 let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
840 end_timer!(fifth_round_time);
841 let commitments: Vec<_> = circuit_commitments
848 .into_iter()
849 .flatten()
850 .zip_eq(AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_ids.iter()).values())
851 .map(|(c, info)| LabeledCommitment::new_with_info(info, *c))
852 .chain(first_commitments)
853 .chain(second_commitments)
854 .chain(third_commitments)
855 .chain(fourth_commitments)
856 .chain(fifth_commitments)
857 .collect();
858
859 let query_set_time = start_timer!(|| "Constructing query set");
860 let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
861 end_timer!(query_set_time);
862
863 sponge.absorb_nonnative_field_elements(proof.evaluations.to_field_elements());
864
865 let mut evaluations = Evaluations::new();
866
867 let mut current_circuit_id = "".to_string();
868 let mut circuit_index: i64 = -1;
869
870 for (label, (_point_name, q)) in query_set.to_set() {
871 if AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_ref()) {
872 evaluations.insert((label, q), E::Fr::zero());
873 } else {
874 if label != "g_1" {
875 let circuit_id = CircuitId::from_witness_label(&label).to_string();
876 if circuit_id != current_circuit_id {
877 circuit_index += 1;
878 current_circuit_id = circuit_id;
879 }
880 }
881 let eval = proof
882 .evaluations
883 .get(circuit_index as usize, &label)
884 .ok_or_else(|| AHPError::MissingEval(label.clone()))?;
885 evaluations.insert((label, q), eval);
886 }
887 }
888
889 let lc_time = start_timer!(|| "Constructing linear combinations");
890 let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
891 &public_inputs,
892 &evaluations,
893 &proof.third_msg,
894 &proof.fourth_msg,
895 &verifier_state,
896 )?;
897 end_timer!(lc_time);
898
899 let pc_time = start_timer!(|| "Checking linear combinations with PC");
900 let evaluations_are_correct = SonicKZG10::<E, FS>::check_combinations(
901 universal_verifier,
902 lc_s.values(),
903 &commitments,
904 &query_set.to_set(),
905 &evaluations,
906 &proof.pc_proof,
907 &mut sponge,
908 )?;
909 end_timer!(pc_time);
910
911 if !evaluations_are_correct {
912 #[cfg(debug_assertions)]
913 eprintln!("SonicKZG10::Check failed using final challenge: {:?}", verifier_state.gamma);
914 }
915
916 end_timer!(verifier_time, || format!(
917 " SonicKZG10::Check for AHP Verifier linear equations: {}",
918 evaluations_are_correct & proof_has_correct_zk_mode
919 ));
920 Ok(evaluations_are_correct & proof_has_correct_zk_mode)
921 }
922}