halo2_axiom/plonk/verifier/
batch.rs

1use ff::FromUniformBytes;
2use group::ff::Field;
3use halo2curves::CurveAffine;
4use rand_core::OsRng;
5
6use super::{verify_proof, VerificationStrategy};
7use crate::{
8    multicore::{IntoParallelIterator, TryFoldAndReduce},
9    plonk::{Error, VerifyingKey},
10    poly::{
11        commitment::{Params, MSM},
12        ipa::{
13            commitment::{IPACommitmentScheme, ParamsVerifierIPA},
14            msm::MSMIPA,
15            multiopen::VerifierIPA,
16            strategy::GuardIPA,
17        },
18    },
19    transcript::{Blake2bRead, TranscriptReadBuffer},
20};
21
22#[cfg(feature = "multicore")]
23use crate::multicore::{IndexedParallelIterator, ParallelIterator};
24
25/// A proof verification strategy that returns the proof's MSM.
26///
27/// `BatchVerifier` handles the accumulation of the MSMs for the batched proofs.
28#[derive(Debug)]
29struct BatchStrategy<'params, C: CurveAffine> {
30    msm: MSMIPA<'params, C>,
31}
32
33impl<'params, C: CurveAffine>
34    VerificationStrategy<'params, IPACommitmentScheme<C>, VerifierIPA<'params, C>>
35    for BatchStrategy<'params, C>
36{
37    type Output = MSMIPA<'params, C>;
38
39    fn new(params: &'params ParamsVerifierIPA<C>) -> Self {
40        BatchStrategy {
41            msm: MSMIPA::new(params),
42        }
43    }
44
45    fn process(
46        self,
47        f: impl FnOnce(MSMIPA<'params, C>) -> Result<GuardIPA<'params, C>, Error>,
48    ) -> Result<Self::Output, Error> {
49        let guard = f(self.msm)?;
50        Ok(guard.use_challenges())
51    }
52
53    fn finalize(self) -> bool {
54        unreachable!()
55    }
56}
57
58#[derive(Debug)]
59struct BatchItem<C: CurveAffine> {
60    instances: Vec<Vec<Vec<C::ScalarExt>>>,
61    proof: Vec<u8>,
62}
63
64/// A verifier that checks multiple proofs in a batch. **This requires the
65/// `batch` crate feature to be enabled.**
66#[derive(Debug, Default)]
67pub struct BatchVerifier<C: CurveAffine> {
68    items: Vec<BatchItem<C>>,
69}
70
71impl<C: CurveAffine> BatchVerifier<C>
72where
73    C::Scalar: FromUniformBytes<64>,
74{
75    /// Constructs a new batch verifier.
76    pub fn new() -> Self {
77        Self { items: vec![] }
78    }
79
80    /// Adds a proof to the batch.
81    pub fn add_proof(&mut self, instances: Vec<Vec<Vec<C::Scalar>>>, proof: Vec<u8>) {
82        self.items.push(BatchItem { instances, proof })
83    }
84
85    /// Finalizes the batch and checks its validity.
86    ///
87    /// Returns `false` if *some* proof was invalid. If the caller needs to identify
88    /// specific failing proofs, it must re-process the proofs separately.
89    ///
90    /// This uses [`OsRng`] internally instead of taking an `R: RngCore` argument, because
91    /// the internal parallelization requires access to a RNG that is guaranteed to not
92    /// clone its internal state when shared between threads.
93    pub fn finalize(self, params: &ParamsVerifierIPA<C>, vk: &VerifyingKey<C>) -> bool {
94        fn accumulate_msm<'params, C: CurveAffine>(
95            mut acc: MSMIPA<'params, C>,
96            msm: MSMIPA<'params, C>,
97        ) -> MSMIPA<'params, C> {
98            // Scale the MSM by a random factor to ensure that if the existing MSM has
99            // `is_zero() == false` then this argument won't be able to interfere with it
100            // to make it true, with high probability.
101            acc.scale(C::Scalar::random(OsRng));
102
103            acc.add_msm(&msm);
104            acc
105        }
106
107        let final_msm = self
108            .items
109            .into_par_iter()
110            .enumerate()
111            .map(|(i, item)| {
112                let instances: Vec<Vec<_>> = item
113                    .instances
114                    .iter()
115                    .map(|i| i.iter().map(|c| &c[..]).collect())
116                    .collect();
117                let instances: Vec<_> = instances.iter().map(|i| &i[..]).collect();
118
119                let strategy = BatchStrategy::new(params);
120                let mut transcript = Blake2bRead::init(&item.proof[..]);
121                verify_proof(params, vk, strategy, &instances, &mut transcript).map_err(|e| {
122                    tracing::debug!("Batch item {} failed verification: {}", i, e);
123                    e
124                })
125            })
126            .try_fold_and_reduce(
127                || params.empty_msm(),
128                |acc, res| res.map(|proof_msm| accumulate_msm(acc, proof_msm)),
129            );
130
131        match final_msm {
132            Ok(msm) => msm.check(),
133            Err(_) => false,
134        }
135    }
136}