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