Skip to main content

snarkvm_algorithms/snark/varuna/
varuna.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use 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 snarkvm_curves::PairingEngine;
47use snarkvm_fields::{One, PrimeField, ToConstraintField, Zero};
48use snarkvm_utilities::{ToBytes, dev_eprintln, dev_println, 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;
57
58/// The Varuna proof system.
59#[derive(Clone, Debug)]
60pub struct VarunaSNARK<E: PairingEngine, FS: AlgebraicSponge<E::Fq, 2>, SM: SNARKMode>(
61    #[doc(hidden)] PhantomData<(E, FS, SM)>,
62);
63
64impl<E: PairingEngine, FS: AlgebraicSponge<E::Fq, 2>, SM: SNARKMode> VarunaSNARK<E, FS, SM> {
65    /// The personalization string for this protocol.
66    /// Used to personalize the Fiat-Shamir RNG.
67    pub const PROTOCOL_NAME: &'static [u8] = b"VARUNA-2023";
68
69    // TODO: implement optimizations resulting from batching
70    //       (e.g. computing a common set of Lagrange powers, FFT precomputations,
71    // etc)
72    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            // TODO: Add check that c is in the correct mode.
84            // Ensure the universal SRS supports the circuit size.
85            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            // Varuna only needs degree 2 random polynomials.
91            let supported_hiding_bound = 1;
92            let supported_lagrange_sizes = [].into_iter(); // TODO: consider removing lagrange_bases_at_beta_g from CommitterKey
93            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 Rng>; // We do not randomize the commitments
105
106            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        varuna_version: VarunaVersion,
141    ) -> FS {
142        let mut sponge = FS::new_with_parameters(fs_parameters);
143
144        sponge.absorb_bytes(match varuna_version {
145            VarunaVersion::V1 | VarunaVersion::V2 => Self::PROTOCOL_NAME,
146            VarunaVersion::V3 => b"VARUNA-2026-V3",
147        });
148        for (batch_size, inputs) in inputs_and_batch_sizes.values() {
149            sponge.absorb_bytes(&(*batch_size as u64).to_le_bytes());
150            for input in inputs.iter() {
151                sponge.absorb_nonnative_field_elements(input.iter().copied());
152            }
153        }
154        for circuit_specific_commitments in circuit_commitments {
155            sponge.absorb_native_field_elements(circuit_specific_commitments);
156        }
157        sponge
158    }
159
160    fn init_sponge_for_certificate(
161        fs_parameters: &FS::Parameters,
162        verifying_key: &CircuitVerifyingKey<E>,
163    ) -> Result<FS> {
164        let mut sponge = FS::new_with_parameters(fs_parameters);
165        sponge.absorb_bytes(&to_bytes_le![&Self::PROTOCOL_NAME]?);
166        sponge.absorb_bytes(&verifying_key.circuit_info.to_bytes_le()?);
167        sponge.absorb_native_field_elements(&verifying_key.circuit_commitments);
168        sponge.absorb_bytes(&verifying_key.id.0);
169        Ok(sponge)
170    }
171
172    fn absorb_labeled_with_sums(
173        comms: &[LabeledCommitment<Commitment<E>>],
174        sums: &[prover::MatrixSums<E::Fr>],
175        sponge: &mut FS,
176    ) {
177        let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
178        Self::absorb_with_sums(&commitments, sums, sponge)
179    }
180
181    fn absorb_labeled(comms: &[LabeledCommitment<Commitment<E>>], sponge: &mut FS) {
182        let commitments: Vec<_> = comms.iter().map(|c| *c.commitment()).collect();
183        Self::absorb(&commitments, sponge);
184    }
185
186    fn absorb(commitments: &[Commitment<E>], sponge: &mut FS) {
187        let sponge_time = start_timer!(|| "Absorbing commitments");
188        sponge.absorb_native_field_elements(commitments);
189        end_timer!(sponge_time);
190    }
191
192    fn absorb_with_sums(commitments: &[Commitment<E>], sums: &[prover::MatrixSums<E::Fr>], sponge: &mut FS) {
193        let sponge_time = start_timer!(|| "Absorbing commitments and message");
194        Self::absorb(commitments, sponge);
195        Self::absorb_sums(sums, sponge);
196        end_timer!(sponge_time);
197    }
198
199    fn absorb_sums(sums: &[prover::MatrixSums<E::Fr>], sponge: &mut FS) {
200        for sum in sums.iter() {
201            sponge.absorb_nonnative_field_elements([sum.sum_a, sum.sum_b, sum.sum_c]);
202        }
203    }
204}
205
206impl<E: PairingEngine, FS, SM> SNARK for VarunaSNARK<E, FS, SM>
207where
208    E::Fr: PrimeField,
209    E::Fq: PrimeField,
210    FS: AlgebraicSponge<E::Fq, 2>,
211    SM: SNARKMode,
212{
213    type BaseField = E::Fq;
214    type Certificate = Certificate<E>;
215    type FSParameters = FS::Parameters;
216    type FiatShamirRng = FS;
217    type Proof = Proof<E>;
218    type ProvingKey = CircuitProvingKey<E, SM>;
219    type ScalarField = E::Fr;
220    type UniversalProver = UniversalProver<E>;
221    type UniversalSRS = UniversalSRS<E>;
222    type UniversalVerifier = UniversalVerifier<E>;
223    type VerifierInput = [E::Fr];
224    type VerifyingKey = CircuitVerifyingKey<E>;
225
226    fn universal_setup(max_degree: usize) -> Result<Self::UniversalSRS> {
227        let setup_time = start_timer!(|| { format!("Varuna::UniversalSetup with max_degree {max_degree}",) });
228        let srs = SonicKZG10::<E, FS>::load_srs(max_degree).map_err(Into::into);
229        end_timer!(setup_time);
230        srs
231    }
232
233    /// Generates the circuit proving and verifying keys.
234    /// This is a deterministic algorithm that anyone can rerun.
235    fn circuit_setup<C: ConstraintSynthesizer<E::Fr>>(
236        universal_srs: &Self::UniversalSRS,
237        circuit: &C,
238    ) -> Result<(Self::ProvingKey, Self::VerifyingKey)> {
239        let mut circuit_keys = Self::batch_circuit_setup::<C>(universal_srs, &[circuit])?;
240        ensure!(circuit_keys.len() == 1);
241        Ok(circuit_keys.pop().unwrap())
242    }
243
244    /// Prove that the verifying key commitments commit to the indexed circuit's
245    /// polynomials
246    fn prove_vk(
247        universal_prover: &Self::UniversalProver,
248        fs_parameters: &Self::FSParameters,
249        verifying_key: &Self::VerifyingKey,
250        proving_key: &Self::ProvingKey,
251    ) -> Result<Self::Certificate> {
252        // Initialize sponge
253        let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
254        // Compute challenges for linear combination, and the point to evaluate the
255        // polynomials at. The linear combination requires `num_polynomials - 1`
256        // coefficients (since the first coeff is 1), and so we squeeze out
257        // `num_polynomials` points.
258        let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
259        let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
260        let one = E::Fr::one();
261        let linear_combination_challenges = core::iter::once(&one).chain(challenges.iter());
262
263        let circuit_id = std::iter::once(&verifying_key.id);
264        let circuit_poly_info = AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_id);
265
266        // We will construct a linear combination and provide a proof of evaluation of
267        // the lc at `point`.
268        let mut lc = crate::polycommit::sonic_pc::LinearCombination::empty("circuit_check");
269        for (label, &c) in circuit_poly_info.keys().zip(linear_combination_challenges) {
270            lc.add(c, label.clone());
271        }
272
273        let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
274        let committer_key = CommitterUnionKey::union(std::iter::once(proving_key.committer_key.as_ref()));
275
276        let empty_randomness = vec![Randomness::<E>::empty(); 12];
277        let certificate = SonicKZG10::<E, FS>::open_combinations(
278            universal_prover,
279            &committer_key,
280            &[lc],
281            proving_key.circuit.interpolate_matrix_evals()?,
282            &empty_randomness,
283            &query_set,
284            &mut sponge,
285        )?;
286
287        Ok(Self::Certificate::new(certificate))
288    }
289
290    /// Verify that the verifying key commitments commit to the indexed
291    /// circuit's polynomials Verify that the verifying key's circuit_info
292    /// is correct
293    fn verify_vk<C: ConstraintSynthesizer<Self::ScalarField>>(
294        universal_verifier: &Self::UniversalVerifier,
295        fs_parameters: &Self::FSParameters,
296        circuit: &C,
297        verifying_key: &Self::VerifyingKey,
298        certificate: &Self::Certificate,
299    ) -> Result<bool> {
300        // Ensure the VerifyingKey encodes the expected circuit.
301        let circuit_id = &verifying_key.id;
302        let state = AHPForR1CS::<E::Fr, SM>::index_helper(circuit)?;
303        if state.index_info != verifying_key.circuit_info {
304            bail!("Circuit info mismatch, expected {:?}, got {:?}", verifying_key.circuit_info, state.index_info);
305        }
306        if state.id != *circuit_id {
307            bail!("Circuit ID mismatch, expected {:?}, got {:?}.", circuit_id, state.id);
308        }
309
310        // Make sure certificate is not hiding
311        if certificate.pc_proof.is_hiding() {
312            bail!("Certificate should not be hiding");
313        }
314
315        // Initialize sponge.
316        let mut sponge = Self::init_sponge_for_certificate(fs_parameters, verifying_key)?;
317
318        // Compute challenges for linear combination, and the point to evaluate the
319        // polynomials at. The linear combination requires `num_polynomials - 1`
320        // coefficients (since the first coeff is 1), and so we squeeze out
321        // `num_polynomials` points.
322        let mut challenges = sponge.squeeze_nonnative_field_elements(verifying_key.circuit_commitments.len());
323        let point = challenges.pop().ok_or(anyhow!("Failed to squeeze random element"))?;
324        let combiners = core::iter::once(E::Fr::one()).chain(challenges);
325
326        // We will construct a linear combination and provide a proof of evaluation of
327        // the lc at `point`.
328        let (lc, evaluation) =
329            AHPForR1CS::<E::Fr, SM>::evaluate_index_polynomials(state, circuit_id, point, combiners)?;
330
331        ensure!(verifying_key.circuit_commitments.len() == lc.terms.len());
332        let commitments = verifying_key
333            .iter()
334            .cloned()
335            .zip_eq(lc.terms.keys())
336            .map(|(c, label)| LabeledCommitment::new(format!("{label:?}"), c, None))
337            .collect_vec();
338        let evaluations = Evaluations::from_iter([(("circuit_check".into(), point), evaluation)]);
339        let query_set = QuerySet::from_iter([("circuit_check".into(), ("challenge".into(), point))]);
340
341        SonicKZG10::<E, FS>::check_combinations(
342            universal_verifier,
343            &[lc],
344            &commitments,
345            &query_set,
346            &evaluations,
347            &certificate.pc_proof,
348            &mut sponge,
349        )
350    }
351
352    /// This is the main entrypoint for creating proofs.
353    /// You can find a specification of the prover algorithm in:
354    /// <https://github.com/ProvableHQ/protocol-docs>
355    fn prove_batch<C: ConstraintSynthesizer<E::Fr>, R: Rng + CryptoRng>(
356        universal_prover: &Self::UniversalProver,
357        fs_parameters: &Self::FSParameters,
358        varuna_version: VarunaVersion,
359        keys_to_constraints: &BTreeMap<&CircuitProvingKey<E, SM>, &[C]>,
360        zk_rng: &mut R,
361    ) -> Result<Self::Proof> {
362        let prover_time = start_timer!(|| "Varuna::Prover");
363        if keys_to_constraints.is_empty() {
364            bail!(SNARKError::EmptyBatch);
365        }
366
367        let mut circuits_to_constraints = BTreeMap::new();
368        for (pk, constraints) in keys_to_constraints {
369            circuits_to_constraints.insert(pk.circuit.deref(), *constraints);
370        }
371        let prover_state = AHPForR1CS::<_, SM>::init_prover(&circuits_to_constraints, zk_rng)?;
372
373        // extract information from the prover key and state to consume in further
374        // calculations
375        let mut batch_sizes = BTreeMap::new();
376        let mut circuit_infos = BTreeMap::new();
377        let mut inputs_and_batch_sizes = BTreeMap::new();
378        let mut total_instances = 0usize;
379        let mut public_inputs = BTreeMap::new(); // inputs need to live longer than the rest of prover_state
380        let num_unique_circuits = keys_to_constraints.len();
381        let mut circuit_ids = Vec::with_capacity(num_unique_circuits);
382
383        #[cfg(feature = "snark-print")]
384        {
385            // Display the batch sizes and (padded) public inputs
386            let batch_sizes = keys_to_constraints
387                .keys()
388                .map(|pk| {
389                    prover_state
390                        .batch_size(&pk.circuit)
391                        .ok_or(anyhow!("[Varuna::prove_batch] Batch not found for circuit {:?}", pk.circuit.id))
392                })
393                .collect::<Result<Vec<_>>>()?;
394
395            println!("[Varuna::prove_batch] Batch sizes: {batch_sizes:?}\n");
396
397            for (i, (key, batch_size)) in keys_to_constraints.keys().zip(batch_sizes.iter()).enumerate() {
398                println!("  - Circuit {i}: {} ({batch_size} instance(s))\n", key.circuit_verifying_key.id);
399                // Safe: `prover_state` was initialized from the same circuits as
400                // `keys_to_constraints`, so `key.circuit` is always present.
401                for (j, public_input) in prover_state.public_inputs(&key.circuit).unwrap().iter().enumerate() {
402                    println!("    - Instance {j}");
403                    // We prepend the initial constant 1 to facilitate collation with displayed
404                    // verifier inputs
405                    println!("      - 0: {}", E::Fr::one());
406                    for (k, value) in public_input.iter().enumerate() {
407                        println!("      - {}: {value}", k + 1);
408                    }
409                    println!();
410                }
411            }
412        }
413
414        for pk in keys_to_constraints.keys() {
415            let batch_size = prover_state.batch_size(&pk.circuit).ok_or(anyhow!("Batch size not found."))?;
416            let public_input = prover_state.public_inputs(&pk.circuit).ok_or(anyhow!("Public input not found."))?;
417
418            let padded_public_input =
419                prover_state.padded_public_inputs(&pk.circuit).ok_or(anyhow!("Padded public input not found."))?;
420
421            let circuit_id = pk.circuit.id;
422            batch_sizes.insert(circuit_id, batch_size);
423            circuit_infos.insert(circuit_id, &pk.circuit_verifying_key.circuit_info);
424            inputs_and_batch_sizes.insert(circuit_id, (batch_size, padded_public_input));
425            public_inputs.insert(circuit_id, public_input);
426            total_instances = total_instances.saturating_add(batch_size);
427
428            circuit_ids.push(circuit_id);
429        }
430        ensure!(prover_state.total_instances == total_instances);
431
432        let committer_key = CommitterUnionKey::union(keys_to_constraints.keys().map(|pk| pk.committer_key.deref()));
433
434        let circuit_commitments =
435            keys_to_constraints.keys().map(|pk| pk.circuit_verifying_key.circuit_commitments.as_slice());
436        dev_println!("inputs_and_batch_sizes: {inputs_and_batch_sizes:?}");
437        let mut sponge =
438            Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone(), varuna_version);
439
440        // --------------------------------------------------------------------
441        // First round
442
443        let prover_state = AHPForR1CS::<_, SM>::prover_first_round(prover_state, zk_rng)?;
444
445        let first_round_comm_time = start_timer!(|| "Committing to first round polys");
446        let (first_commitments, first_commitment_randomnesses) = {
447            let first_round_oracles = prover_state.first_round_oracles.as_ref().unwrap();
448            SonicKZG10::<E, FS>::commit(
449                universal_prover,
450                &committer_key,
451                first_round_oracles.iter().map(Into::into),
452                SM::ZK.then_some(zk_rng),
453            )?
454        };
455        end_timer!(first_round_comm_time);
456
457        Self::absorb_labeled(&first_commitments, &mut sponge);
458
459        let (verifier_first_message, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
460            &batch_sizes,
461            &circuit_infos,
462            prover_state.max_constraint_domain,
463            prover_state.max_variable_domain,
464            prover_state.max_non_zero_domain,
465            &mut sponge,
466        )?;
467        // --------------------------------------------------------------------
468
469        // --------------------------------------------------------------------
470        // Second round
471
472        let (second_oracles, prover_state) =
473            AHPForR1CS::<_, SM>::prover_second_round(&verifier_first_message, prover_state, zk_rng)?;
474
475        let second_round_comm_time = start_timer!(|| "Committing to second round polys");
476        let (second_commitments, second_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
477            universal_prover,
478            &committer_key,
479            second_oracles.iter().map(Into::into),
480            SM::ZK.then_some(zk_rng),
481        )?;
482        end_timer!(second_round_comm_time);
483
484        Self::absorb_labeled(&second_commitments, &mut sponge);
485
486        let (verifier_second_msg, verifier_state) =
487            AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge, varuna_version)?;
488        // --------------------------------------------------------------------
489
490        // --------------------------------------------------------------------
491        // Preparation for third round
492
493        let (prover_prepare_third_message, prover_state, verifier_prepare_third_msg, verifier_state) = {
494            match varuna_version {
495                VarunaVersion::V1 => (None, prover_state, None, verifier_state),
496                VarunaVersion::V2 | VarunaVersion::V3 => {
497                    let (prover_prepare_third_message, prover_state) = AHPForR1CS::<_, SM>::prover_prepare_third_round(
498                        &verifier_first_message,
499                        &verifier_second_msg,
500                        prover_state,
501                        zk_rng,
502                    )?;
503
504                    Self::absorb_sums(
505                        &prover_prepare_third_message.sums.clone().into_iter().flatten().collect_vec(),
506                        &mut sponge,
507                    );
508
509                    let (verifier_prepare_third_msg, verifier_state) =
510                        AHPForR1CS::<_, SM>::verifier_prepare_third_round(
511                            verifier_state,
512                            &batch_sizes,
513                            &circuit_infos,
514                            &mut sponge,
515                            varuna_version,
516                        )?;
517
518                    (Some(prover_prepare_third_message), prover_state, Some(verifier_prepare_third_msg), verifier_state)
519                }
520            }
521        };
522        // --------------------------------------------------------------------
523
524        // --------------------------------------------------------------------
525        // Third round
526
527        let (prover_third_message, third_oracles, prover_state) = AHPForR1CS::<_, SM>::prover_third_round(
528            &verifier_first_message,
529            &verifier_second_msg,
530            &verifier_prepare_third_msg,
531            prover_state,
532            zk_rng,
533            varuna_version,
534        )?;
535
536        let third_round_comm_time = start_timer!(|| "Committing to third round polys");
537        let (third_commitments, third_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
538            universal_prover,
539            &committer_key,
540            third_oracles.iter().map(Into::into),
541            SM::ZK.then_some(zk_rng),
542        )?;
543        end_timer!(third_round_comm_time);
544
545        match varuna_version {
546            VarunaVersion::V1 => {
547                let prover_third_message = prover_third_message
548                    .clone()
549                    .ok_or_else(|| anyhow!("Expected prover to contribute sums in the third round."))?;
550                if prover_prepare_third_message.is_some() {
551                    return Err(anyhow!("Expected prover to not contribute sums in the prepare third round."))?;
552                }
553                Self::absorb_labeled_with_sums(
554                    &third_commitments,
555                    &prover_third_message.sums.into_iter().flatten().collect_vec(),
556                    &mut sponge,
557                );
558            }
559            VarunaVersion::V2 | VarunaVersion::V3 => {
560                if prover_third_message.is_some() {
561                    return Err(anyhow!("Expected prover to not contribute sums in the third round."))?;
562                }
563                Self::absorb_labeled(&third_commitments, &mut sponge);
564            }
565        }
566
567        // Extract the prover's third message to be used in the verifier's third round.
568        let prover_third_message = match varuna_version {
569            VarunaVersion::V1 => prover_third_message,
570            VarunaVersion::V2 | VarunaVersion::V3 => prover_prepare_third_message,
571        }
572        .ok_or_else(|| anyhow!("Prover did not contribute sums in the expected round."))?;
573
574        let (verifier_third_msg, verifier_state) =
575            AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
576        // --------------------------------------------------------------------
577
578        // --------------------------------------------------------------------
579        // Fourth round
580
581        let (prover_fourth_message, fourth_oracles, mut prover_state) =
582            AHPForR1CS::<_, SM>::prover_fourth_round(&verifier_second_msg, &verifier_third_msg, prover_state, zk_rng)?;
583
584        let fourth_round_comm_time = start_timer!(|| "Committing to fourth round polys");
585        let (fourth_commitments, fourth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
586            universal_prover,
587            &committer_key,
588            fourth_oracles.iter().map(Into::into),
589            SM::ZK.then_some(zk_rng),
590        )?;
591        end_timer!(fourth_round_comm_time);
592
593        Self::absorb_labeled_with_sums(&fourth_commitments, &prover_fourth_message.sums, &mut sponge);
594
595        let (verifier_fourth_msg, verifier_state) =
596            AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
597        // --------------------------------------------------------------------
598
599        // We take out values from state before they are consumed.
600        let first_round_oracles = prover_state.first_round_oracles.take().unwrap();
601        let index_a_polys =
602            prover_state.circuit_specific_states.values_mut().flat_map(|s| s.a_polys.take().unwrap()).collect_vec();
603        let index_b_polys =
604            prover_state.circuit_specific_states.values_mut().flat_map(|s| s.b_polys.take().unwrap()).collect_vec();
605
606        // --------------------------------------------------------------------
607        // Fifth round
608        let fifth_oracles = AHPForR1CS::<_, SM>::prover_fifth_round(verifier_fourth_msg, prover_state, zk_rng)?;
609
610        let fifth_round_comm_time = start_timer!(|| "Committing to fifth round polys");
611        let (fifth_commitments, fifth_commitment_randomnesses) = SonicKZG10::<E, FS>::commit(
612            universal_prover,
613            &committer_key,
614            fifth_oracles.iter().map(Into::into),
615            SM::ZK.then_some(zk_rng),
616        )?;
617        end_timer!(fifth_round_comm_time);
618
619        Self::absorb_labeled(&fifth_commitments, &mut sponge);
620
621        let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
622        // --------------------------------------------------------------------
623
624        // Gather prover polynomials in one vector.
625        let polynomials: Vec<_> = index_a_polys
626            .into_iter()
627            .chain(index_b_polys)
628            .chain(first_round_oracles.into_iter())
629            .chain(second_oracles.into_iter())
630            .chain(third_oracles.into_iter())
631            .chain(fourth_oracles.into_iter())
632            .chain(fifth_oracles.into_iter())
633            .collect();
634        ensure!(
635            polynomials.len()
636                == num_unique_circuits * 6 + // numerator and denominator for each matrix sumcheck
637            AHPForR1CS::<E::Fr, SM>::num_first_round_oracles(total_instances) +
638            AHPForR1CS::<E::Fr, SM>::num_second_round_oracles() +
639            AHPForR1CS::<E::Fr, SM>::num_third_round_oracles() +
640            AHPForR1CS::<E::Fr, SM>::num_fourth_round_oracles(num_unique_circuits) +
641            AHPForR1CS::<E::Fr, SM>::num_fifth_round_oracles()
642        );
643
644        // Gather commitments in one vector.
645        let witness_comm_len = if SM::ZK { first_commitments.len() - 1 } else { first_commitments.len() };
646        let mask_poly = SM::ZK.then(|| *first_commitments[witness_comm_len].commitment());
647        let witness_commitments = first_commitments[..witness_comm_len]
648            .iter()
649            .map(|c| proof::WitnessCommitments { w: *c.commitment() })
650            .collect_vec();
651        let fourth_commitments_chunked = fourth_commitments.chunks_exact(3);
652        let (g_a_commitments, g_b_commitments, g_c_commitments) = fourth_commitments_chunked
653            .map(|c| (*c[0].commitment(), *c[1].commitment(), *c[2].commitment()))
654            .multiunzip();
655
656        #[rustfmt::skip]
657        let commitments = proof::Commitments {
658            witness_commitments,
659            mask_poly,
660            h_0: *second_commitments[0].commitment(),
661            g_1: *third_commitments[0].commitment(),
662            h_1: *third_commitments[1].commitment(),
663            g_a_commitments,
664            g_b_commitments,
665            g_c_commitments,
666            h_2: *fifth_commitments[0].commitment(),
667        };
668
669        // Gather commitment randomness together.
670        let indexer_randomness = vec![Randomness::<E>::empty(); 6 * num_unique_circuits];
671        let commitment_randomnesses: Vec<Randomness<E>> = indexer_randomness
672            .into_iter()
673            .chain(first_commitment_randomnesses)
674            .chain(second_commitment_randomnesses)
675            .chain(third_commitment_randomnesses)
676            .chain(fourth_commitment_randomnesses)
677            .chain(fifth_commitment_randomnesses)
678            .collect();
679
680        let empty_randomness = Randomness::<E>::empty();
681        if SM::ZK {
682            ensure!(commitment_randomnesses.iter().any(|r| r != &empty_randomness));
683        } else {
684            ensure!(commitment_randomnesses.iter().all(|r| r == &empty_randomness));
685        }
686
687        // Compute the AHP verifier's query set.
688        let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
689        dev_println!("Final challenge gamma: {:?}", verifier_state.gamma);
690        let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
691            &public_inputs,
692            &polynomials,
693            &prover_third_message,
694            &prover_fourth_message,
695            &verifier_state,
696            varuna_version,
697        )?;
698
699        let eval_time = start_timer!(|| "Evaluating linear combinations over query set");
700        let mut evaluations = std::collections::BTreeMap::new();
701        for (label, (_, point)) in query_set.to_set() {
702            if !AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_str()) {
703                let lc = lc_s.get(&label).ok_or_else(|| AHPError::MissingEval(label.to_string()))?;
704                let evaluation = polynomials.get_lc_eval(lc, point)?;
705                evaluations.insert(label, evaluation);
706            }
707        }
708
709        let evaluations = proof::Evaluations::from_map(&evaluations, batch_sizes.clone());
710        end_timer!(eval_time);
711
712        sponge.absorb_nonnative_field_elements(evaluations.to_field_elements());
713
714        let pc_proof = SonicKZG10::<E, FS>::open_combinations(
715            universal_prover,
716            &committer_key,
717            lc_s.values(),
718            polynomials,
719            &commitment_randomnesses,
720            &query_set.to_set(),
721            &mut sponge,
722        )?;
723
724        let proof = Proof::<E>::new(
725            batch_sizes,
726            commitments,
727            evaluations,
728            prover_third_message,
729            prover_fourth_message,
730            pc_proof,
731        )?;
732        proof.check_batch_sizes()?;
733        ensure!(proof.pc_proof.is_hiding() == SM::ZK);
734
735        end_timer!(prover_time);
736        Ok(proof)
737    }
738
739    /// This is the main entrypoint for verifying proofs.
740    /// You can find a specification of the verifier algorithm in:
741    /// <https://github.com/ProvableHQ/protocol-docs>
742    fn verify_batch<B: Borrow<Self::VerifierInput>>(
743        universal_verifier: &Self::UniversalVerifier,
744        fs_parameters: &Self::FSParameters,
745        varuna_version: VarunaVersion,
746        keys_to_inputs: &BTreeMap<&Self::VerifyingKey, &[B]>,
747        proof: &Self::Proof,
748    ) -> Result<bool> {
749        if keys_to_inputs.is_empty() {
750            bail!(SNARKError::EmptyBatch);
751        }
752
753        proof.check_batch_sizes()?;
754        let batch_sizes_vec = proof.batch_sizes();
755        let mut batch_sizes = BTreeMap::new();
756        ensure!(
757            keys_to_inputs.len() == batch_sizes_vec.len(),
758            "[verify batch] Expected {} keys to inputs, but {} were provided.",
759            batch_sizes_vec.len(),
760            keys_to_inputs.len()
761        );
762        for (i, (vk, public_inputs_i)) in keys_to_inputs.iter().enumerate() {
763            batch_sizes.insert(vk.id, batch_sizes_vec[i]);
764
765            if public_inputs_i.is_empty() {
766                bail!(SNARKError::EmptyBatch);
767            }
768
769            if public_inputs_i.len() != batch_sizes_vec[i] {
770                bail!(SNARKError::BatchSizeMismatch);
771            }
772        }
773
774        // collect values into structures for our calculations
775        let mut max_num_constraints = 0;
776        let mut max_num_variables = 0;
777        let mut max_non_zero_domain = None;
778        let mut public_inputs = BTreeMap::new();
779        let mut padded_public_vec = Vec::with_capacity(keys_to_inputs.len());
780        let mut inputs_and_batch_sizes = BTreeMap::new();
781        let mut input_domains = BTreeMap::new();
782        let mut circuit_infos = BTreeMap::new();
783        let mut circuit_ids = Vec::with_capacity(keys_to_inputs.len());
784
785        #[cfg(feature = "snark-print")]
786        {
787            // Display the batch sizes and (padded) public inputs
788            println!(
789                "[Varuna::verify_batch] Batch sizes: {:?}\n",
790                keys_to_inputs.values().map(|instances| instances.len()).collect_vec()
791            );
792
793            for (i, (circuit, public_inputs)) in keys_to_inputs.iter().enumerate() {
794                println!("  - Circuit {i}: {} ({} instance(s))\n", circuit.id, public_inputs.len());
795                for (j, public_input) in public_inputs.iter().enumerate() {
796                    let public_input = public_input.borrow().to_field_elements()?;
797                    println!("    - Instance {j}");
798                    for (k, value) in public_input.iter().enumerate() {
799                        println!("      - {k}: {value}");
800                    }
801                    println!("\n");
802                }
803            }
804        }
805
806        for (&vk, &public_inputs_i) in keys_to_inputs.iter() {
807            max_num_constraints = max_num_constraints.max(vk.circuit_info.num_constraints);
808            max_num_variables = max_num_variables.max(vk.circuit_info.num_public_and_private_variables);
809
810            let non_zero_domains = AHPForR1CS::<_, SM>::cmp_non_zero_domains(&vk.circuit_info, max_non_zero_domain)?;
811            max_non_zero_domain = non_zero_domains.max_non_zero_domain;
812
813            let input_domain = EvaluationDomain::<E::Fr>::new(vk.circuit_info.num_public_inputs)
814                .ok_or(anyhow!("Failed to create EvaluationDomain from num_public_inputs"))?;
815            input_domains.insert(vk.id, input_domain);
816
817            let input_fields = public_inputs_i
818                .iter()
819                .map(|input| {
820                    let input = input.borrow().to_field_elements()?;
821                    ensure!(input.len() > 0);
822                    ensure!(input[0] == E::Fr::one());
823                    if input.len() > input_domain.size() {
824                        bail!(SNARKError::PublicInputSizeMismatch);
825                    }
826                    Ok(input)
827                })
828                .collect::<Result<Vec<_>, _>>()?;
829
830            let (padded_public_inputs_i, parsed_public_inputs_i): (Vec<_>, Vec<_>) = {
831                input_fields
832                    .iter()
833                    .map(|input| {
834                        let input_len = input.len().max(input_domain.size());
835                        let mut new_input = Vec::with_capacity(input_len);
836                        new_input.extend_from_slice(input);
837                        new_input.resize(input_len, E::Fr::zero());
838                        dev_println!("[verify Batch] Number of padded public variables: {}", new_input.len());
839                        let unformatted = prover::ConstraintSystem::unformat_public_input(&new_input);
840                        (new_input, unformatted)
841                    })
842                    .unzip()
843            };
844
845            let circuit_id = vk.id;
846            public_inputs.insert(circuit_id, parsed_public_inputs_i);
847
848            padded_public_vec.push(padded_public_inputs_i);
849
850            circuit_infos.insert(circuit_id, &vk.circuit_info);
851            circuit_ids.push(circuit_id);
852        }
853        for (i, (vk, &batch_size)) in keys_to_inputs.keys().zip(batch_sizes.values()).enumerate() {
854            inputs_and_batch_sizes.insert(vk.id, (batch_size, padded_public_vec[i].as_slice()));
855        }
856        let max_constraint_domain =
857            EvaluationDomain::<E::Fr>::new(max_num_constraints).ok_or(SynthesisError::PolyTooLarge)?;
858        let max_variable_domain =
859            EvaluationDomain::<E::Fr>::new(max_num_variables).ok_or(SynthesisError::PolyTooLarge)?;
860        let max_non_zero_domain = max_non_zero_domain.ok_or(SynthesisError::PolyTooLarge)?;
861
862        let comms = &proof.commitments;
863        let proof_has_correct_zk_mode = if SM::ZK {
864            proof.pc_proof.is_hiding() & comms.mask_poly.is_some()
865        } else {
866            !proof.pc_proof.is_hiding() & comms.mask_poly.is_none()
867        };
868        if !proof_has_correct_zk_mode {
869            dev_eprintln!(
870                "Found `mask_poly` in the first round when not expected, or proof has incorrect hiding mode ({})",
871                proof.pc_proof.is_hiding()
872            );
873            return Ok(false);
874        }
875
876        let verifier_time = start_timer!(|| format!("Varuna::Verify with batch sizes: {batch_sizes:?}"));
877
878        let first_round_info = AHPForR1CS::<E::Fr, SM>::first_round_polynomial_info(batch_sizes.iter());
879
880        let mut first_comms_consumed = 0;
881        let mut first_commitments = batch_sizes
882            .iter()
883            .flat_map(|(circuit_id, &batch_size)| {
884                let first_comms = comms.witness_commitments[first_comms_consumed..][..batch_size]
885                    .iter()
886                    .enumerate()
887                    .map(|(j, w_comm)| {
888                        LabeledCommitment::new_with_info(
889                            &first_round_info[&witness_label(*circuit_id, "w", j)],
890                            w_comm.w,
891                        )
892                    });
893                first_comms_consumed += batch_size;
894                first_comms
895            })
896            .collect_vec();
897
898        if SM::ZK {
899            first_commitments.push(LabeledCommitment::new_with_info(
900                first_round_info.get("mask_poly").ok_or(anyhow!("Missing mask_poly"))?,
901                comms.mask_poly.ok_or(anyhow!("Missing mask_poly"))?,
902            ));
903        }
904
905        let second_round_info = AHPForR1CS::<E::Fr, SM>::second_round_polynomial_info();
906        let second_commitments = [LabeledCommitment::new_with_info(&second_round_info["h_0"], comms.h_0)];
907
908        let third_round_info = AHPForR1CS::<E::Fr, SM>::third_round_polynomial_info(max_variable_domain.size());
909        let third_commitments = [
910            LabeledCommitment::new_with_info(&third_round_info["g_1"], comms.g_1),
911            LabeledCommitment::new_with_info(&third_round_info["h_1"], comms.h_1),
912        ];
913
914        ensure!(
915            comms.g_a_commitments.len() == comms.g_b_commitments.len(),
916            "[verify Batch] Expected {} g_a commitments to match {} g_b commitments.",
917            comms.g_b_commitments.len(),
918            comms.g_a_commitments.len()
919        );
920        ensure!(
921            comms.g_a_commitments.len() == comms.g_c_commitments.len(),
922            "[verify Batch] Expected {} g_a commitments to match {} g_c commitments.",
923            comms.g_c_commitments.len(),
924            comms.g_a_commitments.len()
925        );
926        ensure!(
927            comms.g_a_commitments.len() == circuit_ids.len(),
928            "[verify Batch] Expected {} g_a commitments to match {} circuit ids.",
929            circuit_ids.len(),
930            comms.g_a_commitments.len()
931        );
932        let fourth_round_info =
933            AHPForR1CS::<E::Fr, SM>::fourth_round_polynomial_info(circuit_infos.clone().into_iter());
934        let fourth_commitments = comms
935            .g_a_commitments
936            .iter()
937            .zip_eq(comms.g_b_commitments.iter())
938            .zip_eq(comms.g_c_commitments.iter())
939            .zip_eq(circuit_ids.iter())
940            .flat_map(|(((g_a, g_b), g_c), circuit_id)| {
941                [
942                    LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_a", 0)], *g_a),
943                    LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_b", 0)], *g_b),
944                    LabeledCommitment::new_with_info(&fourth_round_info[&witness_label(*circuit_id, "g_c", 0)], *g_c),
945                ]
946            })
947            .collect_vec();
948
949        let fifth_round_info = AHPForR1CS::<E::Fr, SM>::fifth_round_polynomial_info();
950        let fifth_commitments = [LabeledCommitment::new_with_info(&fifth_round_info["h_2"], comms.h_2)];
951
952        let circuit_commitments = keys_to_inputs.keys().map(|vk| vk.circuit_commitments.as_slice());
953        dev_println!("inputs_and_batch_sizes: {inputs_and_batch_sizes:?}");
954        let mut sponge =
955            Self::init_sponge(fs_parameters, &inputs_and_batch_sizes, circuit_commitments.clone(), varuna_version);
956
957        // --------------------------------------------------------------------
958        // First round
959        let first_round_time = start_timer!(|| "First round");
960        Self::absorb_labeled(&first_commitments, &mut sponge);
961        let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_first_round(
962            &batch_sizes,
963            &circuit_infos,
964            max_constraint_domain,
965            max_variable_domain,
966            max_non_zero_domain,
967            &mut sponge,
968        )?;
969        end_timer!(first_round_time);
970        // --------------------------------------------------------------------
971
972        // --------------------------------------------------------------------
973        // Second round
974        let second_round_time = start_timer!(|| "Second round");
975        Self::absorb_labeled(&second_commitments, &mut sponge);
976        let (_, verifier_state) =
977            AHPForR1CS::<_, SM>::verifier_second_round(verifier_state, &mut sponge, varuna_version)?;
978        end_timer!(second_round_time);
979        // --------------------------------------------------------------------
980
981        // --------------------------------------------------------------------
982        // Prep third round
983        let verifier_state = {
984            match varuna_version {
985                VarunaVersion::V1 => verifier_state,
986                VarunaVersion::V2 | VarunaVersion::V3 => {
987                    let prepare_third_round_time = start_timer!(|| "Prep third round");
988                    Self::absorb_sums(&proof.third_msg.sums.clone().into_iter().flatten().collect_vec(), &mut sponge);
989                    let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_prepare_third_round(
990                        verifier_state,
991                        &batch_sizes,
992                        &circuit_infos,
993                        &mut sponge,
994                        varuna_version,
995                    )?;
996                    end_timer!(prepare_third_round_time);
997                    verifier_state
998                }
999            }
1000        };
1001        // --------------------------------------------------------------------
1002
1003        // --------------------------------------------------------------------
1004        // Third round
1005        let third_round_time = start_timer!(|| "Third round");
1006        match varuna_version {
1007            VarunaVersion::V1 => {
1008                Self::absorb_labeled_with_sums(
1009                    &third_commitments,
1010                    &proof.third_msg.sums.clone().into_iter().flatten().collect_vec(),
1011                    &mut sponge,
1012                );
1013            }
1014            VarunaVersion::V2 | VarunaVersion::V3 => {
1015                Self::absorb_labeled(&third_commitments, &mut sponge);
1016            }
1017        }
1018        let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_third_round(verifier_state, &mut sponge)?;
1019        end_timer!(third_round_time);
1020        // --------------------------------------------------------------------
1021
1022        // --------------------------------------------------------------------
1023        // Fourth round
1024        let fourth_round_time = start_timer!(|| "Fourth round");
1025
1026        Self::absorb_labeled_with_sums(&fourth_commitments, &proof.fourth_msg.sums, &mut sponge);
1027        let (_, verifier_state) = AHPForR1CS::<_, SM>::verifier_fourth_round(verifier_state, &mut sponge)?;
1028        end_timer!(fourth_round_time);
1029        // --------------------------------------------------------------------
1030
1031        // --------------------------------------------------------------------
1032        // Fifth round
1033        let fifth_round_time = start_timer!(|| "Fifth round");
1034
1035        Self::absorb_labeled(&fifth_commitments, &mut sponge);
1036        let verifier_state = AHPForR1CS::<_, SM>::verifier_fifth_round(verifier_state, &mut sponge)?;
1037        end_timer!(fifth_round_time);
1038        // --------------------------------------------------------------------
1039
1040        // Collect degree bounds for commitments. Indexed polynomials have *no*
1041        // degree bounds because we know the committed index polynomial has the
1042        // correct degree.
1043
1044        ensure!(
1045            circuit_commitments.len() == circuit_ids.len(),
1046            "[verify Batch] Expected {} circuit commitments, but {} were provided.",
1047            circuit_ids.len(),
1048            circuit_commitments.len()
1049        );
1050        let commitments: Vec<_> = circuit_commitments
1051            .into_iter()
1052            .flatten()
1053            .zip_eq(AHPForR1CS::<E::Fr, SM>::index_polynomial_info(circuit_ids.iter()).values())
1054            .map(|(c, info)| LabeledCommitment::new_with_info(info, *c))
1055            .chain(first_commitments)
1056            .chain(second_commitments)
1057            .chain(third_commitments)
1058            .chain(fourth_commitments)
1059            .chain(fifth_commitments)
1060            .collect();
1061
1062        let query_set_time = start_timer!(|| "Constructing query set");
1063        let (query_set, verifier_state) = AHPForR1CS::<_, SM>::verifier_query_set(verifier_state);
1064        end_timer!(query_set_time);
1065
1066        sponge.absorb_nonnative_field_elements(proof.evaluations.to_field_elements());
1067
1068        let mut evaluations = Evaluations::new();
1069
1070        let mut current_circuit_id = "".to_string();
1071        let mut circuit_index: i64 = -1;
1072
1073        for (label, (_point_name, q)) in query_set.to_set() {
1074            if AHPForR1CS::<E::Fr, SM>::LC_WITH_ZERO_EVAL.contains(&label.as_ref()) {
1075                evaluations.insert((label, q), E::Fr::zero());
1076            } else {
1077                if label != "g_1" {
1078                    let circuit_id = CircuitId::from_witness_label(&label).to_string();
1079                    if circuit_id != current_circuit_id {
1080                        circuit_index += 1;
1081                        current_circuit_id = circuit_id;
1082                    }
1083                }
1084                let eval = proof
1085                    .evaluations
1086                    .get(circuit_index as usize, &label)
1087                    .ok_or_else(|| AHPError::MissingEval(label.clone()))?;
1088                evaluations.insert((label, q), eval);
1089            }
1090        }
1091
1092        let lc_time = start_timer!(|| "Constructing linear combinations");
1093        let lc_s = AHPForR1CS::<_, SM>::construct_linear_combinations(
1094            &public_inputs,
1095            &evaluations,
1096            &proof.third_msg,
1097            &proof.fourth_msg,
1098            &verifier_state,
1099            varuna_version,
1100        )?;
1101        end_timer!(lc_time);
1102
1103        let pc_time = start_timer!(|| "Checking linear combinations with PC");
1104        let evaluations_are_correct = SonicKZG10::<E, FS>::check_combinations(
1105            universal_verifier,
1106            lc_s.values(),
1107            &commitments,
1108            &query_set.to_set(),
1109            &evaluations,
1110            &proof.pc_proof,
1111            &mut sponge,
1112        )?;
1113        end_timer!(pc_time);
1114
1115        if !evaluations_are_correct {
1116            dev_eprintln!("SonicKZG10::Check failed using final challenge gamma: {:?}", verifier_state.gamma);
1117        }
1118
1119        end_timer!(verifier_time, || format!(
1120            " SonicKZG10::Check for AHP Verifier linear equations: {}",
1121            evaluations_are_correct & proof_has_correct_zk_mode
1122        ));
1123        Ok(evaluations_are_correct & proof_has_correct_zk_mode)
1124    }
1125}