mirror of https://github.com/zcash/halo2.git
123 lines
4.0 KiB
Rust
123 lines
4.0 KiB
Rust
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<C>) -> Self {
|
|
BatchStrategy {
|
|
msm: MSM::new(params),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'params, C: CurveAffine> VerificationStrategy<'params, C> for BatchStrategy<'params, C> {
|
|
type Output = MSM<'params, C>;
|
|
|
|
fn process<E: EncodedChallenge<C>>(
|
|
self,
|
|
f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
|
|
) -> Result<Self::Output, Error> {
|
|
let guard = f(self.msm)?;
|
|
Ok(guard.use_challenges())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct BatchItem<C: CurveAffine> {
|
|
instances: Vec<Vec<Vec<C::Scalar>>>,
|
|
proof: Vec<u8>,
|
|
}
|
|
|
|
/// A verifier that checks multiple proofs in a batch. **This requires the
|
|
/// `batch` crate feature to be enabled.**
|
|
#[derive(Debug, Default)]
|
|
pub struct BatchVerifier<C: CurveAffine> {
|
|
items: Vec<BatchItem<C>>,
|
|
}
|
|
|
|
impl<C: CurveAffine> BatchVerifier<C> {
|
|
/// 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<Vec<Vec<C::Scalar>>>, proof: Vec<u8>) {
|
|
self.items.push(BatchItem { instances, proof })
|
|
}
|
|
}
|
|
|
|
impl<C: CurveAffine> BatchVerifier<C>
|
|
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<C>, vk: &VerifyingKey<C>) -> 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<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,
|
|
}
|
|
}
|
|
}
|