use group::ff::{Field, FromUniformBytes}; use pasta_curves::arithmetic::CurveAffine; use rand_core::OsRng; use super::{verify_proof, VerificationStrategy}; use crate::{ multicore::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}, plonk::{Error, VerifyingKey}, poly::commitment::{Guard, Params, MSM}, transcript::{Blake2bRead, EncodedChallenge}, }; /// A proof verification strategy that returns the proof's MSM. /// /// `BatchVerifier` handles the accumulation of the MSMs for the batched proofs. #[derive(Debug)] struct BatchStrategy<'params, C: CurveAffine> { msm: MSM<'params, C>, } impl<'params, C: CurveAffine> BatchStrategy<'params, C> { fn new(params: &'params Params) -> Self { BatchStrategy { msm: MSM::new(params), } } } impl<'params, C: CurveAffine> VerificationStrategy<'params, C> for BatchStrategy<'params, C> { type Output = MSM<'params, C>; fn process>( self, f: impl FnOnce(MSM<'params, C>) -> Result, Error>, ) -> Result { let guard = f(self.msm)?; Ok(guard.use_challenges()) } } #[derive(Debug)] struct BatchItem { instances: Vec>>, proof: Vec, } /// A verifier that checks multiple proofs in a batch. **This requires the /// `batch` crate feature to be enabled.** #[derive(Debug, Default)] pub struct BatchVerifier { items: Vec>, } impl BatchVerifier { /// Constructs a new batch verifier. pub fn new() -> Self { Self { items: vec![] } } /// Adds a proof to the batch. pub fn add_proof(&mut self, instances: Vec>>, proof: Vec) { self.items.push(BatchItem { instances, proof }) } } impl BatchVerifier where C::Scalar: FromUniformBytes<64>, { /// Finalizes the batch and checks its validity. /// /// Returns `false` if *some* proof was invalid. If the caller needs to identify /// specific failing proofs, it must re-process the proofs separately. /// /// This uses [`OsRng`] internally instead of taking an `R: RngCore` argument, because /// the internal parallelization requires access to a RNG that is guaranteed to not /// clone its internal state when shared between threads. pub fn finalize(self, params: &Params, vk: &VerifyingKey) -> bool { fn accumulate_msm<'params, C: CurveAffine>( mut acc: MSM<'params, C>, msm: MSM<'params, C>, ) -> MSM<'params, C> { // Scale the MSM by a random factor to ensure that if the existing MSM has // `is_zero() == false` then this argument won't be able to interfere with it // to make it true, with high probability. acc.scale(C::Scalar::random(OsRng)); acc.add_msm(&msm); acc } let final_msm = self .items .into_par_iter() .enumerate() .map(|(i, item)| { let instances: Vec> = item .instances .iter() .map(|i| i.iter().map(|c| &c[..]).collect()) .collect(); let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect(); let strategy = BatchStrategy::new(params); let mut transcript = Blake2bRead::init(&item.proof[..]); verify_proof(params, vk, strategy, &instances, &mut transcript).map_err(|e| { tracing::debug!("Batch item {} failed verification: {}", i, e); e }) }) .try_fold( || params.empty_msm(), |msm, res| res.map(|proof_msm| accumulate_msm(msm, proof_msm)), ) .try_reduce(|| params.empty_msm(), |a, b| Ok(accumulate_msm(a, b))); match final_msm { Ok(msm) => msm.eval(), Err(_) => false, } } }