mirror of https://github.com/zcash/halo2.git
Merge pull request #472 from zcash/separate-single-and-batch-verification
halo2_proofs: Improve `plonk::verify_proof` API
This commit is contained in:
commit
bb56139414
|
@ -9,13 +9,19 @@ and this project adheres to Rust's notion of
|
||||||
(relative to `halo2 0.1.0-beta.1`)
|
(relative to `halo2 0.1.0-beta.1`)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- `halo2_proofs::plonk`:
|
||||||
|
- `VerificationStrategy`
|
||||||
|
- `SingleVerifier`, an implementation of `VerificationStrategy` for verifying
|
||||||
|
proofs individually.
|
||||||
|
- `BatchVerifier`, an implementation of `VerificationStrategy` for verifying
|
||||||
|
multiple proofs in a batch.
|
||||||
- `halo2_proofs::dev::FailureLocation` (used in `VerifyFailure::Lookup`)
|
- `halo2_proofs::dev::FailureLocation` (used in `VerifyFailure::Lookup`)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `halo2_proofs` now depends on `rand_core` instead of `rand`, and requires the
|
- `halo2_proofs::plonk::verify_proof` now takes a `VerificationStrategy` instead
|
||||||
caller to provide the specific RNG implementation:
|
of an `MSM` directly.
|
||||||
- `halo2_proofs::plonk::{create_proof, verify_proof}` now take an argument
|
- `halo2_proofs` now depends on `rand_core` instead of `rand`.
|
||||||
`R: rand_core::RngCore`.
|
- `halo2_proofs::plonk::create_proof` now take an argument `R: rand_core::RngCore`.
|
||||||
- `halo2_proofs::plonk::Error` has been overhauled:
|
- `halo2_proofs::plonk::Error` has been overhauled:
|
||||||
- `Error` now implements `std::fmt::Display` and `std::error::Error`.
|
- `Error` now implements `std::fmt::Display` and `std::error::Error`.
|
||||||
- `Error` no longer implements `PartialEq`. Tests can check for specific error
|
- `Error` no longer implements `PartialEq`. Tests can check for specific error
|
||||||
|
|
|
@ -268,12 +268,9 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verifier(params: &Params<EqAffine>, vk: &VerifyingKey<EqAffine>, proof: &[u8]) {
|
fn verifier(params: &Params<EqAffine>, vk: &VerifyingKey<EqAffine>, proof: &[u8]) {
|
||||||
let rng = OsRng;
|
let strategy = SingleVerifier::new(params);
|
||||||
let msm = params.empty_msm();
|
|
||||||
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof);
|
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(proof);
|
||||||
let guard = verify_proof(params, vk, msm, &[&[]], rng, &mut transcript).unwrap();
|
assert!(verify_proof(params, vk, strategy, &[&[]], &mut transcript).is_ok());
|
||||||
let msm = guard.clone().use_challenges();
|
|
||||||
assert!(msm.eval());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let k_range = 8..=16;
|
let k_range = 8..=16;
|
||||||
|
|
|
@ -14,21 +14,111 @@ use crate::poly::{
|
||||||
};
|
};
|
||||||
use crate::transcript::{read_n_points, read_n_scalars, EncodedChallenge, TranscriptRead};
|
use crate::transcript::{read_n_points, read_n_scalars, EncodedChallenge, TranscriptRead};
|
||||||
|
|
||||||
|
/// Trait representing a strategy for verifying Halo 2 proofs.
|
||||||
|
pub trait VerificationStrategy<'params, C: CurveAffine> {
|
||||||
|
/// The output type of this verification strategy after processing a proof.
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
/// Obtains an MSM from the verifier strategy and yields back the strategy's
|
||||||
|
/// output.
|
||||||
|
fn process<E: EncodedChallenge<C>>(
|
||||||
|
self,
|
||||||
|
f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
|
||||||
|
) -> Result<Self::Output, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A verifier that checks a single proof at a time.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SingleVerifier<'params, C: CurveAffine> {
|
||||||
|
msm: MSM<'params, C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine> SingleVerifier<'params, C> {
|
||||||
|
/// Constructs a new single proof verifier.
|
||||||
|
pub fn new(params: &'params Params<C>) -> Self {
|
||||||
|
SingleVerifier {
|
||||||
|
msm: MSM::new(params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine> VerificationStrategy<'params, C> for SingleVerifier<'params, C> {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
let msm = guard.use_challenges();
|
||||||
|
if msm.eval() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::ConstraintSystemFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A verifier that checks multiple proofs in a batch.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BatchVerifier<'params, C: CurveAffine, R: RngCore> {
|
||||||
|
msm: MSM<'params, C>,
|
||||||
|
rng: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine, R: RngCore> BatchVerifier<'params, C, R> {
|
||||||
|
/// Constructs a new batch verifier.
|
||||||
|
pub fn new(params: &'params Params<C>, rng: R) -> Self {
|
||||||
|
BatchVerifier {
|
||||||
|
msm: MSM::new(params),
|
||||||
|
rng,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[must_use]
|
||||||
|
pub fn finalize(self) -> bool {
|
||||||
|
self.msm.eval()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine, R: RngCore> VerificationStrategy<'params, C>
|
||||||
|
for BatchVerifier<'params, C, R>
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn process<E: EncodedChallenge<C>>(
|
||||||
|
mut self,
|
||||||
|
f: impl FnOnce(MSM<'params, C>) -> Result<Guard<'params, C, E>, Error>,
|
||||||
|
) -> Result<Self::Output, Error> {
|
||||||
|
// 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.
|
||||||
|
self.msm.scale(C::Scalar::random(&mut self.rng));
|
||||||
|
|
||||||
|
let guard = f(self.msm)?;
|
||||||
|
let msm = guard.use_challenges();
|
||||||
|
Ok(Self { msm, rng: self.rng })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a boolean indicating whether or not the proof is valid
|
/// Returns a boolean indicating whether or not the proof is valid
|
||||||
pub fn verify_proof<
|
pub fn verify_proof<
|
||||||
'params,
|
'params,
|
||||||
C: CurveAffine,
|
C: CurveAffine,
|
||||||
E: EncodedChallenge<C>,
|
E: EncodedChallenge<C>,
|
||||||
R: RngCore,
|
|
||||||
T: TranscriptRead<C, E>,
|
T: TranscriptRead<C, E>,
|
||||||
|
V: VerificationStrategy<'params, C>,
|
||||||
>(
|
>(
|
||||||
params: &'params Params<C>,
|
params: &'params Params<C>,
|
||||||
vk: &VerifyingKey<C>,
|
vk: &VerifyingKey<C>,
|
||||||
msm: MSM<'params, C>,
|
strategy: V,
|
||||||
instances: &[&[&[C::Scalar]]],
|
instances: &[&[&[C::Scalar]]],
|
||||||
rng: R,
|
|
||||||
transcript: &mut T,
|
transcript: &mut T,
|
||||||
) -> Result<Guard<'params, C, E>, Error> {
|
) -> Result<V::Output, Error> {
|
||||||
// Check that instances matches the expected number of instance columns
|
// Check that instances matches the expected number of instance columns
|
||||||
for instances in instances.iter() {
|
for instances in instances.iter() {
|
||||||
if instances.len() != vk.cs.num_instance_columns {
|
if instances.len() != vk.cs.num_instance_columns {
|
||||||
|
@ -293,5 +383,7 @@ pub fn verify_proof<
|
||||||
|
|
||||||
// We are now convinced the circuit is satisfied so long as the
|
// We are now convinced the circuit is satisfied so long as the
|
||||||
// polynomial commitments open to the correct values.
|
// polynomial commitments open to the correct values.
|
||||||
multiopen::verify_proof(params, rng, transcript, queries, msm).map_err(|_| Error::Opening)
|
strategy.process(|msm| {
|
||||||
|
multiopen::verify_proof(params, transcript, queries, msm).map_err(|_| Error::Opening)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -323,7 +323,6 @@ fn test_roundtrip() {
|
||||||
|
|
||||||
let guard = verify_proof(
|
let guard = verify_proof(
|
||||||
¶ms,
|
¶ms,
|
||||||
rng,
|
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
std::iter::empty()
|
std::iter::empty()
|
||||||
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
|
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
|
||||||
|
@ -346,7 +345,6 @@ fn test_roundtrip() {
|
||||||
|
|
||||||
let guard = verify_proof(
|
let guard = verify_proof(
|
||||||
¶ms,
|
¶ms,
|
||||||
rng,
|
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
std::iter::empty()
|
std::iter::empty()
|
||||||
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
|
.chain(Some(VerifierQuery::new_commitment(&a, x, avx)))
|
||||||
|
|
|
@ -19,11 +19,9 @@ pub fn verify_proof<
|
||||||
I,
|
I,
|
||||||
C: CurveAffine,
|
C: CurveAffine,
|
||||||
E: EncodedChallenge<C>,
|
E: EncodedChallenge<C>,
|
||||||
R: RngCore,
|
|
||||||
T: TranscriptRead<C, E>,
|
T: TranscriptRead<C, E>,
|
||||||
>(
|
>(
|
||||||
params: &'params Params<C>,
|
params: &'params Params<C>,
|
||||||
rng: R,
|
|
||||||
transcript: &mut T,
|
transcript: &mut T,
|
||||||
queries: I,
|
queries: I,
|
||||||
mut msm: MSM<'params, C>,
|
mut msm: MSM<'params, C>,
|
||||||
|
@ -31,11 +29,6 @@ pub fn verify_proof<
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = VerifierQuery<'r, 'params, C>> + Clone,
|
I: IntoIterator<Item = VerifierQuery<'r, 'params, C>> + Clone,
|
||||||
{
|
{
|
||||||
// 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.
|
|
||||||
msm.scale(C::Scalar::random(rng));
|
|
||||||
|
|
||||||
// Sample x_1 for compressing openings at the same point sets together
|
// Sample x_1 for compressing openings at the same point sets together
|
||||||
let x_1: ChallengeX1<_> = transcript.squeeze_challenge_scalar();
|
let x_1: ChallengeX1<_> = transcript.squeeze_challenge_scalar();
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
#![allow(clippy::op_ref)]
|
#![allow(clippy::op_ref)]
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use halo2_proofs::arithmetic::FieldExt;
|
use halo2_proofs::arithmetic::{CurveAffine, FieldExt};
|
||||||
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner};
|
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner};
|
||||||
use halo2_proofs::dev::MockProver;
|
use halo2_proofs::dev::MockProver;
|
||||||
use halo2_proofs::pasta::{Eq, EqAffine, Fp};
|
use halo2_proofs::pasta::{Eq, EqAffine, Fp};
|
||||||
use halo2_proofs::plonk::{
|
use halo2_proofs::plonk::{
|
||||||
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, ConstraintSystem,
|
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, BatchVerifier, Circuit, Column,
|
||||||
Error, Fixed, TableColumn, VerifyingKey,
|
ConstraintSystem, Error, Fixed, SingleVerifier, TableColumn, VerificationStrategy,
|
||||||
|
VerifyingKey,
|
||||||
};
|
};
|
||||||
|
use halo2_proofs::poly::commitment::{Guard, MSM};
|
||||||
use halo2_proofs::poly::{commitment::Params, Rotation};
|
use halo2_proofs::poly::{commitment::Params, Rotation};
|
||||||
use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite, Challenge255};
|
use halo2_proofs::transcript::{Blake2bRead, Blake2bWrite, Challenge255, EncodedChallenge};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
@ -449,50 +451,110 @@ fn plonk_api() {
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let msm = params.empty_msm();
|
// Test single-verifier strategy.
|
||||||
|
{
|
||||||
|
let strategy = SingleVerifier::new(¶ms);
|
||||||
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
||||||
let guard = verify_proof(
|
assert!(verify_proof(
|
||||||
¶ms,
|
¶ms,
|
||||||
pk.get_vk(),
|
pk.get_vk(),
|
||||||
msm,
|
strategy,
|
||||||
|
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
||||||
|
&mut transcript,
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test accumulation-based strategy.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct AccumulationVerifier<'params, C: CurveAffine> {
|
||||||
|
msm: MSM<'params, C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine> AccumulationVerifier<'params, C> {
|
||||||
|
fn new(params: &'params Params<C>) -> Self {
|
||||||
|
AccumulationVerifier {
|
||||||
|
msm: MSM::new(params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'params, C: CurveAffine> VerificationStrategy<'params, C>
|
||||||
|
for AccumulationVerifier<'params, C>
|
||||||
|
{
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
let g = guard.compute_g();
|
||||||
|
let (msm, _) = guard.use_g(g);
|
||||||
|
if msm.eval() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::ConstraintSystemFailure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let strategy = AccumulationVerifier::new(¶ms);
|
||||||
|
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
||||||
|
assert!(verify_proof(
|
||||||
|
¶ms,
|
||||||
|
pk.get_vk(),
|
||||||
|
strategy,
|
||||||
|
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
||||||
|
&mut transcript,
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test batch-verifier strategy.
|
||||||
|
//
|
||||||
|
|
||||||
|
{
|
||||||
|
let strategy = BatchVerifier::new(¶ms, OsRng);
|
||||||
|
|
||||||
|
// First proof.
|
||||||
|
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
||||||
|
let strategy = verify_proof(
|
||||||
|
¶ms,
|
||||||
|
pk.get_vk(),
|
||||||
|
strategy,
|
||||||
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
||||||
OsRng,
|
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
{
|
|
||||||
let msm = guard.clone().use_challenges();
|
// Write and then read the verification key in between (to check round-trip
|
||||||
assert!(msm.eval());
|
// serialization).
|
||||||
}
|
// TODO: Figure out whether https://github.com/zcash/halo2/issues/449 should
|
||||||
{
|
// be caught by this, or if it is caused by downstream changes to halo2.
|
||||||
let g = guard.compute_g();
|
|
||||||
let (msm, _) = guard.clone().use_g(g);
|
|
||||||
assert!(msm.eval());
|
|
||||||
}
|
|
||||||
let msm = guard.clone().use_challenges();
|
|
||||||
assert!(msm.clone().eval());
|
|
||||||
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
|
||||||
let mut vk_buffer = vec![];
|
let mut vk_buffer = vec![];
|
||||||
pk.get_vk().write(&mut vk_buffer).unwrap();
|
pk.get_vk().write(&mut vk_buffer).unwrap();
|
||||||
let vk = VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], ¶ms)
|
let vk =
|
||||||
|
VerifyingKey::<EqAffine>::read::<_, MyCircuit<Fp>>(&mut &vk_buffer[..], ¶ms)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let guard = verify_proof(
|
|
||||||
|
// "Second" proof (just the first proof again).
|
||||||
|
let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]);
|
||||||
|
let strategy = verify_proof(
|
||||||
¶ms,
|
¶ms,
|
||||||
&vk,
|
&vk,
|
||||||
msm,
|
strategy,
|
||||||
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
&[&[&pubinputs[..]], &[&pubinputs[..]]],
|
||||||
OsRng,
|
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
{
|
|
||||||
let msm = guard.clone().use_challenges();
|
// Check the batch.
|
||||||
assert!(msm.eval());
|
assert!(strategy.finalize());
|
||||||
}
|
|
||||||
{
|
|
||||||
let g = guard.compute_g();
|
|
||||||
let (msm, _) = guard.clone().use_g(g);
|
|
||||||
assert!(msm.eval());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue