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, dev_eprintln, dev_println, 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;
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>>(
74 universal_srs: &UniversalSRS<E>,
75 circuits: &[&C],
76 ) -> Result<Vec<(CircuitProvingKey<E, SM>, CircuitVerifyingKey<E>)>> {
77 let index_time = start_timer!(|| "Varuna::CircuitSetup");
78
79 let universal_prover = &universal_srs.to_universal_prover()?;
80
81 let mut circuit_keys = Vec::with_capacity(circuits.len());
82 for circuit in circuits {
83 let mut indexed_circuit = AHPForR1CS::<_, SM>::index(*circuit)?;
84 universal_srs.download_powers_for(0..indexed_circuit.max_degree()?).map_err(|e| {
87 anyhow!("Failed to download powers for degree {}: {e}", indexed_circuit.max_degree().unwrap())
88 })?;
89 let coefficient_support = AHPForR1CS::<E::Fr, SM>::get_degree_bounds(&indexed_circuit.index_info)?;
90
91 let supported_hiding_bound = 1;
93 let supported_lagrange_sizes = [].into_iter(); let (committer_key, _) = SonicKZG10::<E, FS>::trim(
95 universal_srs,
96 indexed_circuit.max_degree()?,
97 supported_lagrange_sizes,
98 supported_hiding_bound,
99 Some(coefficient_support.as_slice()),
100 )?;
101
102 let ck = CommitterUnionKey::union(std::iter::once(&committer_key));
103
104 let commit_time = start_timer!(|| format!("Commit to index polynomials for {}", indexed_circuit.id));
105 let setup_rng = None::<&mut dyn RngCore>; let (mut circuit_commitments, commitment_randomnesses): (_, _) = SonicKZG10::<E, FS>::commit(
108 universal_prover,
109 &ck,
110 indexed_circuit.interpolate_matrix_evals()?.map(Into::into),
111 setup_rng,
112 )?;
113 let empty_randomness = Randomness::<E>::empty();
114 ensure!(commitment_randomnesses.iter().all(|r| r == &empty_randomness));
115 end_timer!(commit_time);
116
117 circuit_commitments.sort_by(|c1, c2| c1.label().cmp(c2.label()));
118 let circuit_commitments = circuit_commitments.into_iter().map(|c| *c.commitment()).collect();
119 indexed_circuit.prune_row_col_evals();
120 let circuit_verifying_key = CircuitVerifyingKey {
121 circuit_info: indexed_circuit.index_info,
122 circuit_commitments,
123 id: indexed_circuit.id,
124 };
125 let circuit_proving_key = CircuitProvingKey {
126 circuit_verifying_key: circuit_verifying_key.clone(),
127 circuit: Arc::new(indexed_circuit),
128 committer_key: Arc::new(committer_key),
129 };
130 circuit_keys.push((circuit_proving_key, circuit_verifying_key));
131 }
132
133 end_timer!(index_time);
134 Ok(circuit_keys)
135 }
136
137 fn init_sponge<'a>(
138 fs_parameters: &FS::Parameters,
139 inputs_and_batch_sizes: &BTreeMap<CircuitId, (usize, &[Vec<E::Fr>])>,
140 circuit_commitments: impl Iterator<Item = &'a [crate::polycommit::sonic_pc::Commitment<E>]>,
141 ) -> FS {
142 let mut sponge = FS::new_with_parameters(fs_parameters);
143 sponge.absorb_bytes(Self::PROTOCOL_NAME);
144 for (batch_size, inputs) in inputs_and_batch_sizes.values() {
145 sponge.absorb_bytes(&(*batch_size as u64).to_le_bytes());
146 for input in inputs.iter() {
147 sponge.absorb_nonnative_field_elements(input.iter().copied());
148 }
149 }
150 for circuit_specific_commitments in circuit_commitments {
151 sponge.absorb_native_field_elements(circuit_specific_commitments);
152 }
153 sponge
154 }
155
156 fn init_sponge_for_certificate(
157 fs_parameters: &FS::Parameters,
158 verifying_key: &CircuitVerifyingKey<E>,
159 ) -> Result<FS> {
160 let mut sponge = FS::new_with_parameters(fs_parameters);
161 sponge.absorb_bytes(&to_bytes_le![&Self::PROTOCOL_NAME]?);
162 sponge.absorb_bytes(&verifying_key.circuit_info.to_bytes_le()?);
163 sponge.absorb_native_field_elements(&verifying_key.circuit_commitments);
164 sponge.absorb_bytes(&verifying_key.id.0);
165 Ok(sponge)
166 }
167
168 fn absorb_labeled_with_sums(
169 comms: &[LabeledCommitment<Commitment<E>>],
170 sums: &[prover::MatrixSums<E::Fr>],
171 sponge: &mut FS,
172 ) {
173 let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
174 Self::absorb_with_sums(&commitments, sums, sponge)
175 }
176
177 fn absorb_labeled(comms: &[LabeledCommitment<Commitment<E>>], sponge: &mut FS) {
178 let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
179 Self::absorb(&commitments, sponge);
180 }
181
182 fn absorb(commitments: &[Commitment<E>], sponge: &mut FS) {
183 let sponge_time = start_timer!(|| "Absorbing commitments");
184 sponge.absorb_native_field_elements(commitments);
185 end_timer!(sponge_time);
186 }
187
188 fn absorb_with_sums(commitments: &[Commitment<E>], sums: &[prover::MatrixSums<E::Fr>], sponge: &mut FS) {
189 let sponge_time = start_timer!(|| "Absorbing commitments and message");
190 Self::absorb(commitments, sponge);
191 Self::absorb_sums(sums, sponge);
192 end_timer!(sponge_time);
193 }
194
195 fn absorb_sums(sums: &[prover::MatrixSums<E::Fr>], sponge: &mut FS) {
196 for sum in sums.iter() {
197 sponge.absorb_nonnative_field_elements([sum.sum_a, sum.sum_b, sum.sum_c]);
198 }
199 }
200}
201
202impl<E: PairingEngine, FS, SM> SNARK for VarunaSNARK<E, FS, SM>
203where
204 E::Fr: PrimeField,
205 E::Fq: PrimeField,
206 FS: AlgebraicSponge<E::Fq, 2>,
207 SM: SNARKMode,
208{
209 type BaseField = E::Fq;
210 type Certificate = Certificate<E>;
211 type FSParameters = FS::Parameters;
212 type FiatShamirRng = FS;
213 type Proof = Proof<E>;
214 type ProvingKey = CircuitProvingKey<E, SM>;
215 type ScalarField = E::Fr;
216 type UniversalProver = UniversalProver<E>;
217 type UniversalSRS = UniversalSRS<E>;
218 type UniversalVerifier = UniversalVerifier<E>;
219 type VerifierInput = [E::Fr];
220 type VerifyingKey = CircuitVerifyingKey<E>;
221
222 fn universal_setup(max_degree: usize) -> Result<Self::UniversalSRS> {
223 let setup_time = start_timer!(|| { format!("Varuna::UniversalSetup with max_degree {max_degree}",) });
224 let srs = SonicKZG10::<E, FS>::load_srs(max_degree).map_err(Into::into);
225 end_timer!(setup_time);
226 srs
227 }
228
229 fn circuit_setup<C: ConstraintSynthesizer<E::Fr>>(
232 universal_srs: &Self::UniversalSRS,
233 circuit: &C,
234 ) -> Result<(Self::ProvingKey, Self::VerifyingKey)> {
235 let mut circuit_keys = Self::batch_circuit_setup::<C>(universal_srs, &[circuit])?;
236 ensure!(circuit_keys.len() == 1);
237 Ok(circuit_keys.pop().unwrap())
238 }
239
240 fn prove_vk(
243 universal_prover: &Self::UniversalProver,
244 fs_parameters: &Self::FSParameters,
245 verifying_key: &Self::VerifyingKey,
246 proving_key: &Self::ProvingKey,
247 ) -> Result<Self::Certificate> {
248 let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
250 let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
255 let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
256 let one = E::Fr::one();
257 let linear_combination_challenges = core::iter::once(&one).chain(challenges.iter());
258
259 let circuit_id = std::iter::once(&verifying_key.id);
260 let circuit_poly_info = AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_id);
261
262 let mut lc = crate::polycommit::sonic_pc::LinearCombination::empty("circuit_check");
265 for (label, &c) in circuit_poly_info.keys().zip(linear_combination_challenges) {
266 lc.add(c, label.clone());
267 }
268
269 let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
270 let committer_key = CommitterUnionKey::union(std::iter::once(proving_key.committer_key.as_ref()));
271
272 let empty_randomness = vec![Randomness::<E>::empty(); 12];
273 let certificate = SonicKZG10::<E, FS>::open_combinations(
274 universal_prover,
275 &committer_key,
276 &[lc],
277 proving_key.circuit.interpolate_matrix_evals()?,
278 &empty_randomness,
279 &query_set,
280 &mut sponge,
281 )?;
282
283 Ok(Self::Certificate::new(certificate))
284 }
285
286 fn verify_vk<C: ConstraintSynthesizer<Self::ScalarField>>(
290 universal_verifier: &Self::UniversalVerifier,
291 fs_parameters: &Self::FSParameters,
292 circuit: &C,
293 verifying_key: &Self::VerifyingKey,
294 certificate: &Self::Certificate,
295 ) -> Result<bool> {
296 let circuit_id = &verifying_key.id;
298 let state = AHPForR1CS::<E::Fr, SM>::index_helper(circuit)?;
299 if state.index_info != verifying_key.circuit_info {
300 bail!("Circuit info mismatch, expected {:?}, got {:?}", verifying_key.circuit_info, state.index_info);
301 }
302 if state.id != *circuit_id {
303 bail!("Circuit ID mismatch, expected {:?}, got {:?}.", circuit_id, state.id);
304 }
305
306 if certificate.pc_proof.is_hiding() {
308 bail!("Certificate should not be hiding");
309 }
310
311 let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
313
314 let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
319 let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
320 let combiners = core::iter::once(E::Fr::one()).chain(challenges);
321
322 let (lc, evaluation) =
325 AHPForR1CS::<E::Fr, SM>::evaluate_index_polynomials(state, circuit_id, point, combiners)?;
326
327 ensure!(verifying_key.circuit_commitments.len() == lc.terms.len());
328 let commitments = verifying_key
329 .iter()
330 .cloned()
331 .zip_eq(lc.terms.keys())
332 .map(|(c, label)| LabeledCommitment::new(format!("{label:?}"), c, None))
333 .collect_vec();
334 let evaluations = Evaluations::from_iter([(("circuit_check".into(), point), evaluation)]);
335 let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
336
337 SonicKZG10::<E, FS>::check_combinations(
338 universal_verifier,
339 &[lc],
340 &commitments,
341 &query_set,
342 &evaluations,
343 &certificate.pc_proof,
344 &mut sponge,
345 )
346 }
347
348 fn prove_batch<C: ConstraintSynthesizer<E::Fr>, R: Rng + CryptoRng>(
352 universal_prover: &Self::UniversalProver,
353 fs_parameters: &Self::FSParameters,
354 varuna_version: VarunaVersion,
355 keys_to_constraints: &BTreeMap<&CircuitProvingKey<E, SM>, &[C]>,
356 zk_rng: &mut R,
357 ) -> Result<Self::Proof> {
358 let prover_time = start_timer!(|| "Varuna::Prover");
359 if keys_to_constraints.is_empty() {
360 bail!(SNARKError::EmptyBatch);
361 }
362
363 let mut circuits_to_constraints = BTreeMap::new();
364 for (pk, constraints) in keys_to_constraints {
365 circuits_to_constraints.insert(pk.circuit.deref(), *constraints);
366 }
367 let prover_state = AHPForR1CS::<_, SM>::init_prover(&circuits_to_constraints, zk_rng)?;
368
369 let mut batch_sizes = BTreeMap::new();
372 let mut circuit_infos = BTreeMap::new();
373 let mut inputs_and_batch_sizes = BTreeMap::new();
374 let mut total_instances = 0usize;
375 let mut public_inputs = BTreeMap::new(); let num_unique_circuits = keys_to_constraints.len();
377 let mut circuit_ids = Vec::with_capacity(num_unique_circuits);
378
379 #[cfg(feature = "snark-print")]
380 {
381 let batch_sizes = keys_to_constraints
383 .keys()
384 .map(|pk| {
385 prover_state
386 .batch_size(&pk.circuit)
387 .ok_or(anyhow!("[Varuna::prove_batch] Batch not found for circuit {:?}", pk.circuit.id))
388 })
389 .collect::<Result<Vec<_>>>()?;
390
391 println!("[Varuna::prove_batch] Batch sizes: {batch_sizes:?}\n");
392
393 for (i, (key, batch_size)) in keys_to_constraints.keys().zip(batch_sizes.iter()).enumerate() {
394 println!(" - Circuit {i}: {} ({batch_size} instance(s))\n", key.circuit_verifying_key.id);
395 for (j, public_input) in prover_state.public_inputs(&key.circuit).unwrap().iter().enumerate() {
398 println!(" - Instance {j}");
399 println!(" - 0: {}", E::Fr::one());
402 for (k, value) in public_input.iter().enumerate() {
403 println!(" - {}: {value}", k + 1);
404 }
405 println!();
406 }
407 }
408 }
409
410 for pk in keys_to_constraints.keys() {
411 let batch_size = prover_state.batch_size(&pk.circuit).ok_or(anyhow!("Batch size not found."))?;
412 let public_input = prover_state.public_inputs(&pk.circuit).ok_or(anyhow!("Public input not found."))?;
413
414 let padded_public_input =
415 prover_state.padded_public_inputs(&pk.circuit).ok_or(anyhow!("Padded public input not found."))?;
416
417 let circuit_id = pk.circuit.id;
418 batch_sizes.insert(circuit_id, batch_size);
419 circuit_infos.insert(circuit_id, &pk.circuit_verifying_key.circuit_info);
420 inputs_and_batch_sizes.insert(circuit_id, (batch_size, padded_public_input));
421 public_inputs.insert(circuit_id, public_input);
422 total_instances = total_instances.saturating_add(batch_size);
423
424 circuit_ids.push(circuit_id);
425 }
426 ensure!(prover_state.total_instances == total_instances);
427
428 let committer_key = CommitterUnionKey::union(keys_to_constraints.keys().map(|pk| pk.committer_key.deref()));
429
430 let circuit_commitments =
431 keys_to_constraints.keys().map(|pk| pk.circuit_verifying_key.circuit_commitments.as_slice());
432 dev_println!("inputs_and_batch_sizes: {inputs_and_batch_sizes:?}");
433 let mut sponge = Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone());
434
435 let prover_state = AHPForR1CS::<_, SM>::prover_first_round(prover_state, zk_rng)?;
439
440 let first_round_comm_time = start_timer!(|| "Committing to first round polys");
441 let (first_commitments, first_commitment_randomnesses) = {
442 let first_round_oracles = prover_state.first_round_oracles.as_ref().unwrap();
443 SonicKZG10::<E, FS>::commit(
444 universal_prover,
445 &committer_key,
446 first_round_oracles.iter().map(Into::into),
447 SM::ZK.then_some(zk_rng),
448 )?
449 };
450 end_timer!(first_round_comm_time);
451
452 Self::absorb_labeled(&first_commitments, &mut sponge);
453
454 let (verifier_first_message, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
455 &batch_sizes,
456 &circuit_infos,
457 prover_state.max_constraint_domain,
458 prover_state.max_variable_domain,
459 prover_state.max_non_zero_domain,
460 &mut sponge,
461 )?;
462 let (second_oracles, prover_state) =
468 AHPForR1CS::<_, SM>::prover_second_round(&verifier_first_message, prover_state, zk_rng)?;
469
470 let second_round_comm_time = start_timer!(|| "Committing to second round polys");
471 let (second_commitments, second_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
472 universal_prover,
473 &committer_key,
474 second_oracles.iter().map(Into::into),
475 SM::ZK.then_some(zk_rng),
476 )?;
477 end_timer!(second_round_comm_time);
478
479 Self::absorb_labeled(&second_commitments, &mut sponge);
480
481 let (verifier_second_msg, verifier_state) =
482 AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge, varuna_version)?;
483 let (prover_prepare_third_message, prover_state, verifier_prepare_third_msg, verifier_state) = {
489 match varuna_version {
490 VarunaVersion::V1 => (None, prover_state, None, verifier_state),
491 VarunaVersion::V2 => {
492 let (prover_prepare_third_message, prover_state) = AHPForR1CS::<_, SM>::prover_prepare_third_round(
493 &verifier_first_message,
494 &verifier_second_msg,
495 prover_state,
496 zk_rng,
497 )?;
498
499 Self::absorb_sums(
500 &prover_prepare_third_message.sums.clone().into_iter().flatten().collect_vec(),
501 &mut sponge,
502 );
503
504 let (verifier_prepare_third_msg, verifier_state) =
505 AHPForR1CS::<_, SM>::verifier_prepare_third_round(
506 verifier_state,
507 &batch_sizes,
508 &circuit_infos,
509 &mut sponge,
510 )?;
511
512 (Some(prover_prepare_third_message), prover_state, Some(verifier_prepare_third_msg), verifier_state)
513 }
514 }
515 };
516 let (prover_third_message, third_oracles, prover_state) = AHPForR1CS::<_, SM>::prover_third_round(
522 &verifier_first_message,
523 &verifier_second_msg,
524 &verifier_prepare_third_msg,
525 prover_state,
526 zk_rng,
527 varuna_version,
528 )?;
529
530 let third_round_comm_time = start_timer!(|| "Committing to third round polys");
531 let (third_commitments, third_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
532 universal_prover,
533 &committer_key,
534 third_oracles.iter().map(Into::into),
535 SM::ZK.then_some(zk_rng),
536 )?;
537 end_timer!(third_round_comm_time);
538
539 match varuna_version {
540 VarunaVersion::V1 => {
541 let prover_third_message = prover_third_message
542 .clone()
543 .ok_or_else(|| anyhow!("Expected prover to contribute sums in the third round."))?;
544 if prover_prepare_third_message.is_some() {
545 return Err(anyhow!("Expected prover to not contribute sums in the prepare third round."))?;
546 }
547 Self::absorb_labeled_with_sums(
548 &third_commitments,
549 &prover_third_message.sums.into_iter().flatten().collect_vec(),
550 &mut sponge,
551 );
552 }
553 VarunaVersion::V2 => {
554 if prover_third_message.is_some() {
555 return Err(anyhow!("Expected prover to not contribute sums in the third round."))?;
556 }
557 Self::absorb_labeled(&third_commitments, &mut sponge);
558 }
559 }
560
561 let prover_third_message = match varuna_version {
563 VarunaVersion::V1 => prover_third_message,
564 VarunaVersion::V2 => prover_prepare_third_message,
565 }
566 .ok_or_else(|| anyhow!("Prover did not contribute sums in the expected round."))?;
567
568 let (verifier_third_msg, verifier_state) =
569 AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
570 let (prover_fourth_message, fourth_oracles, mut prover_state) =
576 AHPForR1CS::<_, SM>::prover_fourth_round(&verifier_second_msg, &verifier_third_msg, prover_state, zk_rng)?;
577
578 let fourth_round_comm_time = start_timer!(|| "Committing to fourth round polys");
579 let (fourth_commitments, fourth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
580 universal_prover,
581 &committer_key,
582 fourth_oracles.iter().map(Into::into),
583 SM::ZK.then_some(zk_rng),
584 )?;
585 end_timer!(fourth_round_comm_time);
586
587 Self::absorb_labeled_with_sums(&fourth_commitments, &prover_fourth_message.sums, &mut sponge);
588
589 let (verifier_fourth_msg, verifier_state) =
590 AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
591 let first_round_oracles = prover_state.first_round_oracles.take().unwrap();
595 let index_a_polys =
596 prover_state.circuit_specific_states.values_mut().flat_map(|s| s.a_polys.take().unwrap()).collect_vec();
597 let index_b_polys =
598 prover_state.circuit_specific_states.values_mut().flat_map(|s| s.b_polys.take().unwrap()).collect_vec();
599
600 let fifth_oracles = AHPForR1CS::<_, SM>::prover_fifth_round(verifier_fourth_msg, prover_state, zk_rng)?;
603
604 let fifth_round_comm_time = start_timer!(|| "Committing to fifth round polys");
605 let (fifth_commitments, fifth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
606 universal_prover,
607 &committer_key,
608 fifth_oracles.iter().map(Into::into),
609 SM::ZK.then_some(zk_rng),
610 )?;
611 end_timer!(fifth_round_comm_time);
612
613 Self::absorb_labeled(&fifth_commitments, &mut sponge);
614
615 let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
616 let polynomials: Vec<_> = index_a_polys
620 .into_iter()
621 .chain(index_b_polys)
622 .chain(first_round_oracles.into_iter())
623 .chain(second_oracles.into_iter())
624 .chain(third_oracles.into_iter())
625 .chain(fourth_oracles.into_iter())
626 .chain(fifth_oracles.into_iter())
627 .collect();
628 ensure!(
629 polynomials.len()
630 == num_unique_circuits * 6 + AHPForR1CS::<E::Fr, SM>::num_first_round_oracles(total_instances) +
632 AHPForR1CS::<E::Fr, SM>::num_second_round_oracles() +
633 AHPForR1CS::<E::Fr, SM>::num_third_round_oracles() +
634 AHPForR1CS::<E::Fr, SM>::num_fourth_round_oracles(num_unique_circuits) +
635 AHPForR1CS::<E::Fr, SM>::num_fifth_round_oracles()
636 );
637
638 let witness_comm_len = if SM::ZK { first_commitments.len() - 1 } else { first_commitments.len() };
640 let mask_poly = SM::ZK.then(|| *first_commitments[witness_comm_len].commitment());
641 let witness_commitments = first_commitments[..witness_comm_len]
642 .iter()
643 .map(|c| proof::WitnessCommitments { w: *c.commitment() })
644 .collect_vec();
645 let fourth_commitments_chunked = fourth_commitments.chunks_exact(3);
646 let (g_a_commitments, g_b_commitments, g_c_commitments) = fourth_commitments_chunked
647 .map(|c| (*c[0].commitment(), *c[1].commitment(), *c[2].commitment()))
648 .multiunzip();
649
650 #[rustfmt::skip]
651 let commitments = proof::Commitments {
652 witness_commitments,
653 mask_poly,
654 h_0: *second_commitments[0].commitment(),
655 g_1: *third_commitments[0].commitment(),
656 h_1: *third_commitments[1].commitment(),
657 g_a_commitments,
658 g_b_commitments,
659 g_c_commitments,
660 h_2: *fifth_commitments[0].commitment(),
661 };
662
663 let indexer_randomness = vec![Randomness::<E>::empty(); 6 * num_unique_circuits];
665 let commitment_randomnesses: Vec<Randomness<E>> = indexer_randomness
666 .into_iter()
667 .chain(first_commitment_randomnesses)
668 .chain(second_commitment_randomnesses)
669 .chain(third_commitment_randomnesses)
670 .chain(fourth_commitment_randomnesses)
671 .chain(fifth_commitment_randomnesses)
672 .collect();
673
674 let empty_randomness = Randomness::<E>::empty();
675 if SM::ZK {
676 ensure!(commitment_randomnesses.iter().any(|r| r != &empty_randomness));
677 } else {
678 ensure!(commitment_randomnesses.iter().all(|r| r == &empty_randomness));
679 }
680
681 let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
683 dev_println!("Final challenge gamma: {:?}", verifier_state.gamma);
684 let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
685 &public_inputs,
686 &polynomials,
687 &prover_third_message,
688 &prover_fourth_message,
689 &verifier_state,
690 varuna_version,
691 )?;
692
693 let eval_time = start_timer!(|| "Evaluating linear combinations over query set");
694 let mut evaluations = std::collections::BTreeMap::new();
695 for (label, (_, point)) in query_set.to_set() {
696 if !AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_str()) {
697 let lc = lc_s.get(&label).ok_or_else(|| AHPError::MissingEval(label.to_string()))?;
698 let evaluation = polynomials.get_lc_eval(lc, point)?;
699 evaluations.insert(label, evaluation);
700 }
701 }
702
703 let evaluations = proof::Evaluations::from_map(&evaluations, batch_sizes.clone());
704 end_timer!(eval_time);
705
706 sponge.absorb_nonnative_field_elements(evaluations.to_field_elements());
707
708 let pc_proof = SonicKZG10::<E, FS>::open_combinations(
709 universal_prover,
710 &committer_key,
711 lc_s.values(),
712 polynomials,
713 &commitment_randomnesses,
714 &query_set.to_set(),
715 &mut sponge,
716 )?;
717
718 let proof = Proof::<E>::new(
719 batch_sizes,
720 commitments,
721 evaluations,
722 prover_third_message,
723 prover_fourth_message,
724 pc_proof,
725 )?;
726 proof.check_batch_sizes()?;
727 ensure!(proof.pc_proof.is_hiding() == SM::ZK);
728
729 end_timer!(prover_time);
730 Ok(proof)
731 }
732
733 fn verify_batch<B: Borrow<Self::VerifierInput>>(
737 universal_verifier: &Self::UniversalVerifier,
738 fs_parameters: &Self::FSParameters,
739 varuna_version: VarunaVersion,
740 keys_to_inputs: &BTreeMap<&Self::VerifyingKey, &[B]>,
741 proof: &Self::Proof,
742 ) -> Result<bool> {
743 if keys_to_inputs.is_empty() {
744 bail!(SNARKError::EmptyBatch);
745 }
746
747 proof.check_batch_sizes()?;
748 let batch_sizes_vec = proof.batch_sizes();
749 let mut batch_sizes = BTreeMap::new();
750 ensure!(
751 keys_to_inputs.len() == batch_sizes_vec.len(),
752 "[verify batch] Expected {} keys to inputs, but {} were provided.",
753 batch_sizes_vec.len(),
754 keys_to_inputs.len()
755 );
756 for (i, (vk, public_inputs_i)) in keys_to_inputs.iter().enumerate() {
757 batch_sizes.insert(vk.id, batch_sizes_vec[i]);
758
759 if public_inputs_i.is_empty() {
760 bail!(SNARKError::EmptyBatch);
761 }
762
763 if public_inputs_i.len() != batch_sizes_vec[i] {
764 bail!(SNARKError::BatchSizeMismatch);
765 }
766 }
767
768 let mut max_num_constraints = 0;
770 let mut max_num_variables = 0;
771 let mut max_non_zero_domain = None;
772 let mut public_inputs = BTreeMap::new();
773 let mut padded_public_vec = Vec::with_capacity(keys_to_inputs.len());
774 let mut inputs_and_batch_sizes = BTreeMap::new();
775 let mut input_domains = BTreeMap::new();
776 let mut circuit_infos = BTreeMap::new();
777 let mut circuit_ids = Vec::with_capacity(keys_to_inputs.len());
778
779 #[cfg(feature = "snark-print")]
780 {
781 println!(
783 "[Varuna::verify_batch] Batch sizes: {:?}\n",
784 keys_to_inputs.values().map(|instances| instances.len()).collect_vec()
785 );
786
787 for (i, (circuit, public_inputs)) in keys_to_inputs.iter().enumerate() {
788 println!(" - Circuit {i}: {} ({} instance(s))\n", circuit.id, public_inputs.len());
789 for (j, public_input) in public_inputs.iter().enumerate() {
790 let public_input = public_input.borrow().to_field_elements()?;
791 println!(" - Instance {j}");
792 for (k, value) in public_input.iter().enumerate() {
793 println!(" - {k}: {value}");
794 }
795 println!("\n");
796 }
797 }
798 }
799
800 for (&vk, &public_inputs_i) in keys_to_inputs.iter() {
801 max_num_constraints = max_num_constraints.max(vk.circuit_info.num_constraints);
802 max_num_variables = max_num_variables.max(vk.circuit_info.num_public_and_private_variables);
803
804 let non_zero_domains = AHPForR1CS::<_, SM>::cmp_non_zero_domains(&vk.circuit_info, max_non_zero_domain)?;
805 max_non_zero_domain = non_zero_domains.max_non_zero_domain;
806
807 let input_domain = EvaluationDomain::<E::Fr>::new(vk.circuit_info.num_public_inputs)
808 .ok_or(anyhow!("Failed to create EvaluationDomain from num_public_inputs"))?;
809 input_domains.insert(vk.id, input_domain);
810
811 let input_fields = public_inputs_i
812 .iter()
813 .map(|input| {
814 let input = input.borrow().to_field_elements()?;
815 ensure!(input.len() > 0);
816 ensure!(input[0] == E::Fr::one());
817 if input.len() > input_domain.size() {
818 bail!(SNARKError::PublicInputSizeMismatch);
819 }
820 Ok(input)
821 })
822 .collect::<Result<Vec<_>, _>>()?;
823
824 let (padded_public_inputs_i, parsed_public_inputs_i): (Vec<_>, Vec<_>) = {
825 input_fields
826 .iter()
827 .map(|input| {
828 let input_len = input.len().max(input_domain.size());
829 let mut new_input = Vec::with_capacity(input_len);
830 new_input.extend_from_slice(input);
831 new_input.resize(input_len, E::Fr::zero());
832 dev_println!("[verify Batch] Number of padded public variables: {}", new_input.len());
833 let unformatted = prover::ConstraintSystem::unformat_public_input(&new_input);
834 (new_input, unformatted)
835 })
836 .unzip()
837 };
838
839 let circuit_id = vk.id;
840 public_inputs.insert(circuit_id, parsed_public_inputs_i);
841
842 padded_public_vec.push(padded_public_inputs_i);
843
844 circuit_infos.insert(circuit_id, &vk.circuit_info);
845 circuit_ids.push(circuit_id);
846 }
847 for (i, (vk, &batch_size)) in keys_to_inputs.keys().zip(batch_sizes.values()).enumerate() {
848 inputs_and_batch_sizes.insert(vk.id, (batch_size, padded_public_vec[i].as_slice()));
849 }
850 let max_constraint_domain =
851 EvaluationDomain::<E::Fr>::new(max_num_constraints).ok_or(SynthesisError::PolyTooLarge)?;
852 let max_variable_domain =
853 EvaluationDomain::<E::Fr>::new(max_num_variables).ok_or(SynthesisError::PolyTooLarge)?;
854 let max_non_zero_domain = max_non_zero_domain.ok_or(SynthesisError::PolyTooLarge)?;
855
856 let comms = &proof.commitments;
857 let proof_has_correct_zk_mode = if SM::ZK {
858 proof.pc_proof.is_hiding() & comms.mask_poly.is_some()
859 } else {
860 !proof.pc_proof.is_hiding() & comms.mask_poly.is_none()
861 };
862 if !proof_has_correct_zk_mode {
863 dev_eprintln!(
864 "Found `mask_poly` in the first round when not expected, or proof has incorrect hiding mode ({})",
865 proof.pc_proof.is_hiding()
866 );
867 return Ok(false);
868 }
869
870 let verifier_time = start_timer!(|| format!("Varuna::Verify with batch sizes: {batch_sizes:?}"));
871
872 let first_round_info = AHPForR1CS::<E::Fr, SM>::first_round_polynomial_info(batch_sizes.iter());
873
874 let mut first_comms_consumed = 0;
875 let mut first_commitments = batch_sizes
876 .iter()
877 .flat_map(|(circuit_id, &batch_size)| {
878 let first_comms = comms.witness_commitments[first_comms_consumed..][..batch_size]
879 .iter()
880 .enumerate()
881 .map(|(j, w_comm)| {
882 LabeledCommitment::new_with_info(
883 &first_round_info[&witness_label(*circuit_id, "w", j)],
884 w_comm.w,
885 )
886 });
887 first_comms_consumed += batch_size;
888 first_comms
889 })
890 .collect_vec();
891
892 if SM::ZK {
893 first_commitments.push(LabeledCommitment::new_with_info(
894 first_round_info.get("mask_poly").ok_or(anyhow!("Missing mask_poly"))?,
895 comms.mask_poly.ok_or(anyhow!("Missing mask_poly"))?,
896 ));
897 }
898
899 let second_round_info = AHPForR1CS::<E::Fr, SM>::second_round_polynomial_info();
900 let second_commitments = [LabeledCommitment::new_with_info(&second_round_info["h_0"], comms.h_0)];
901
902 let third_round_info = AHPForR1CS::<E::Fr, SM>::third_round_polynomial_info(max_variable_domain.size());
903 let third_commitments = [
904 LabeledCommitment::new_with_info(&third_round_info["g_1"], comms.g_1),
905 LabeledCommitment::new_with_info(&third_round_info["h_1"], comms.h_1),
906 ];
907
908 ensure!(
909 comms.g_a_commitments.len() == comms.g_b_commitments.len(),
910 "[verify Batch] Expected {} g_a commitments to match {} g_b commitments.",
911 comms.g_b_commitments.len(),
912 comms.g_a_commitments.len()
913 );
914 ensure!(
915 comms.g_a_commitments.len() == comms.g_c_commitments.len(),
916 "[verify Batch] Expected {} g_a commitments to match {} g_c commitments.",
917 comms.g_c_commitments.len(),
918 comms.g_a_commitments.len()
919 );
920 ensure!(
921 comms.g_a_commitments.len() == circuit_ids.len(),
922 "[verify Batch] Expected {} g_a commitments to match {} circuit ids.",
923 circuit_ids.len(),
924 comms.g_a_commitments.len()
925 );
926 let fourth_round_info =
927 AHPForR1CS::<E::Fr, SM>::fourth_round_polynomial_info(circuit_infos.clone().into_iter());
928 let fourth_commitments = comms
929 .g_a_commitments
930 .iter()
931 .zip_eq(comms.g_b_commitments.iter())
932 .zip_eq(comms.g_c_commitments.iter())
933 .zip_eq(circuit_ids.iter())
934 .flat_map(|(((g_a, g_b), g_c), circuit_id)| {
935 [
936 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_a", 0)], *g_a),
937 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_b", 0)], *g_b),
938 LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_c", 0)], *g_c),
939 ]
940 })
941 .collect_vec();
942
943 let fifth_round_info = AHPForR1CS::<E::Fr, SM>::fifth_round_polynomial_info();
944 let fifth_commitments = [LabeledCommitment::new_with_info(&fifth_round_info["h_2"], comms.h_2)];
945
946 let circuit_commitments = keys_to_inputs.keys().map(|vk| vk.circuit_commitments.as_slice());
947 dev_println!("inputs_and_batch_sizes: {inputs_and_batch_sizes:?}");
948 let mut sponge = Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone());
949
950 let first_round_time = start_timer!(|| "First round");
953 Self::absorb_labeled(&first_commitments, &mut sponge);
954 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
955 &batch_sizes,
956 &circuit_infos,
957 max_constraint_domain,
958 max_variable_domain,
959 max_non_zero_domain,
960 &mut sponge,
961 )?;
962 end_timer!(first_round_time);
963 let second_round_time = start_timer!(|| "Second round");
968 Self::absorb_labeled(&second_commitments, &mut sponge);
969 let (_, verifier_state) =
970 AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge, varuna_version)?;
971 end_timer!(second_round_time);
972 let verifier_state = {
977 match varuna_version {
978 VarunaVersion::V1 => verifier_state,
979 VarunaVersion::V2 => {
980 let prepare_third_round_time = start_timer!(|| "Prep third round");
981 Self::absorb_sums(&proof.third_msg.sums.clone().into_iter().flatten().collect_vec(), &mut sponge);
982 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_prepare_third_round(
983 verifier_state,
984 &batch_sizes,
985 &circuit_infos,
986 &mut sponge,
987 )?;
988 end_timer!(prepare_third_round_time);
989 verifier_state
990 }
991 }
992 };
993 let third_round_time = start_timer!(|| "Third round");
998 match varuna_version {
999 VarunaVersion::V1 => {
1000 Self::absorb_labeled_with_sums(
1001 &third_commitments,
1002 &proof.third_msg.sums.clone().into_iter().flatten().collect_vec(),
1003 &mut sponge,
1004 );
1005 }
1006 VarunaVersion::V2 => {
1007 Self::absorb_labeled(&third_commitments, &mut sponge);
1008 }
1009 }
1010 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
1011 end_timer!(third_round_time);
1012 let fourth_round_time = start_timer!(|| "Fourth round");
1017
1018 Self::absorb_labeled_with_sums(&fourth_commitments, &proof.fourth_msg.sums, &mut sponge);
1019 let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
1020 end_timer!(fourth_round_time);
1021 let fifth_round_time = start_timer!(|| "Fifth round");
1026
1027 Self::absorb_labeled(&fifth_commitments, &mut sponge);
1028 let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
1029 end_timer!(fifth_round_time);
1030 ensure!(
1037 circuit_commitments.len() == circuit_ids.len(),
1038 "[verify Batch] Expected {} circuit commitments, but {} were provided.",
1039 circuit_ids.len(),
1040 circuit_commitments.len()
1041 );
1042 let commitments: Vec<_> = circuit_commitments
1043 .into_iter()
1044 .flatten()
1045 .zip_eq(AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_ids.iter()).values())
1046 .map(|(c, info)| LabeledCommitment::new_with_info(info, *c))
1047 .chain(first_commitments)
1048 .chain(second_commitments)
1049 .chain(third_commitments)
1050 .chain(fourth_commitments)
1051 .chain(fifth_commitments)
1052 .collect();
1053
1054 let query_set_time = start_timer!(|| "Constructing query set");
1055 let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
1056 end_timer!(query_set_time);
1057
1058 sponge.absorb_nonnative_field_elements(proof.evaluations.to_field_elements());
1059
1060 let mut evaluations = Evaluations::new();
1061
1062 let mut current_circuit_id = "".to_string();
1063 let mut circuit_index: i64 = -1;
1064
1065 for (label, (_point_name, q)) in query_set.to_set() {
1066 if AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_ref()) {
1067 evaluations.insert((label, q), E::Fr::zero());
1068 } else {
1069 if label != "g_1" {
1070 let circuit_id = CircuitId::from_witness_label(&label).to_string();
1071 if circuit_id != current_circuit_id {
1072 circuit_index += 1;
1073 current_circuit_id = circuit_id;
1074 }
1075 }
1076 let eval = proof
1077 .evaluations
1078 .get(circuit_index as usize, &label)
1079 .ok_or_else(|| AHPError::MissingEval(label.clone()))?;
1080 evaluations.insert((label, q), eval);
1081 }
1082 }
1083
1084 let lc_time = start_timer!(|| "Constructing linear combinations");
1085 let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
1086 &public_inputs,
1087 &evaluations,
1088 &proof.third_msg,
1089 &proof.fourth_msg,
1090 &verifier_state,
1091 varuna_version,
1092 )?;
1093 end_timer!(lc_time);
1094
1095 let pc_time = start_timer!(|| "Checking linear combinations with PC");
1096 let evaluations_are_correct = SonicKZG10::<E, FS>::check_combinations(
1097 universal_verifier,
1098 lc_s.values(),
1099 &commitments,
1100 &query_set.to_set(),
1101 &evaluations,
1102 &proof.pc_proof,
1103 &mut sponge,
1104 )?;
1105 end_timer!(pc_time);
1106
1107 if !evaluations_are_correct {
1108 dev_eprintln!("SonicKZG10::Check failed using final challenge gamma: {:?}", verifier_state.gamma);
1109 }
1110
1111 end_timer!(verifier_time, || format!(
1112 " SonicKZG10::Check for AHP Verifier linear equations: {}",
1113 evaluations_are_correct & proof_has_correct_zk_mode
1114 ));
1115 Ok(evaluations_are_correct & proof_has_correct_zk_mode)
1116 }
1117}