Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to Rust's notion of

## [Unreleased]

### Changed
- The `verify` and `verify_multicore` methods of batch verification for Groth16
now take a `PreparedVerifyingKey` instead for performance improvement.

## [0.13.1] - 2022-07-05
### Added
- `bellman::groth16::batch::Verifier` now has a `verify_multicore` method (when
Expand Down
2 changes: 1 addition & 1 deletion benches/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn bench_batch_verify(c: &mut Criterion) {
for (proof, input) in proofs.iter() {
batch.queue((proof.clone(), vec![*input]));
}
batch.verify(&mut rng, &params.vk)
batch.verify(&mut rng, &pvk)
})
},
);
Expand Down
6 changes: 6 additions & 0 deletions src/groth16/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@ pub struct PreparedVerifyingKey<E: MultiMillerLoop> {
neg_delta_g2: E::G2Prepared,
/// Copy of IC from `VerifiyingKey`.
ic: Vec<E::G1Affine>,

// Batch verification needs
alpha_g1: E::G1Affine,
delta_g2: E::G2Prepared,
gamma_g2: E::G2Prepared,
beta_g2: E::G2Prepared,
}

pub trait ParameterSource<E: Engine> {
Expand Down
5 changes: 5 additions & 0 deletions src/groth16/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ pub fn prepare_verifying_key<E: MultiMillerLoop>(vk: &VerifyingKey<E>) -> Prepar
neg_gamma_g2: gamma.into(),
neg_delta_g2: delta.into(),
ic: vk.ic.clone(),

alpha_g1: vk.alpha_g1,
delta_g2: vk.delta_g2.into(),
gamma_g2: vk.gamma_g2.into(),
beta_g2: vk.beta_g2.into(),
}
}

Expand Down
57 changes: 32 additions & 25 deletions src/groth16/verifier/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use rand_core::OsRng;
use rayon::{iter::ParallelIterator, prelude::ParallelSlice};

use crate::{
groth16::{PreparedVerifyingKey, Proof, VerifyingKey},
groth16::{PreparedVerifyingKey, Proof},
VerificationError,
};

Expand Down Expand Up @@ -96,24 +96,24 @@ where
self.items.push(item.into())
}

/// Perform batch verification with a particular `VerifyingKey`, returning
/// Perform batch verification with a particular `PreparedVerifyingKey`, returning
/// `Ok(())` if all proofs were verified and `VerificationError` otherwise.
#[allow(non_snake_case)]
pub fn verify<R: RngCore + CryptoRng>(
self,
mut rng: R,
vk: &VerifyingKey<E>,
pvk: &PreparedVerifyingKey<E>,
) -> Result<(), VerificationError> {
if self
.items
.iter()
.any(|Item { inputs, .. }| inputs.len() + 1 != vk.ic.len())
.any(|Item { inputs, .. }| inputs.len() + 1 != pvk.ic.len())
{
return Err(VerificationError::InvalidVerifyingKey);
}

let mut ml_terms = Vec::<(E::G1Affine, E::G2Prepared)>::new();
let mut acc_Gammas = vec![E::Fr::zero(); vk.ic.len()];
let mut acc_Gammas = vec![E::Fr::zero(); pvk.ic.len()];
let mut acc_Delta = E::G1::identity();
let mut acc_Y = E::Fr::zero();

Expand All @@ -136,20 +136,27 @@ where
for (a_i, acc_Gamma_i) in Iterator::zip(inputs.iter(), acc_Gammas.iter_mut().skip(1)) {
*acc_Gamma_i += &(z * a_i);
}
// TODO: could use multiexp here across all items
acc_Delta += proof.c * z;
acc_Y += &z;
}

ml_terms.push((acc_Delta.to_affine(), E::G2Prepared::from(vk.delta_g2)));
let mut ml_terms = ml_terms.iter().map(|(a, b)| (a, b)).collect::<Vec<_>>();

let acc_Delta = acc_Delta.to_affine();

let Psi = vk
ml_terms.push((&acc_Delta, &pvk.delta_g2));

// TODO: could use multiexp here
let Psi = pvk
.ic
.iter()
.zip(acc_Gammas.iter())
.map(|(&Psi_i, acc_Gamma_i)| Psi_i * acc_Gamma_i)
.sum();
.sum::<E::G1>()
.to_affine();

ml_terms.push((E::G1Affine::from(Psi), E::G2Prepared::from(vk.gamma_g2)));
ml_terms.push((&Psi, &pvk.gamma_g2));

// Covers the [acc_Y]⋅e(alpha_g1, beta_g2) component
//
Expand All @@ -160,12 +167,11 @@ where
// ([acc_Y]⋅alpha_g1, beta_g2)
// to our Miller loop terms because
// [acc_Y]⋅e(alpha_g1, beta_g2) = e([acc_Y]⋅alpha_g1, beta_g2)
ml_terms.push((
E::G1Affine::from(vk.alpha_g1 * acc_Y),
E::G2Prepared::from(vk.beta_g2),
));

let ml_terms = ml_terms.iter().map(|(a, b)| (a, b)).collect::<Vec<_>>();
// TODO: figure out if it's actually slower to remove this and replace
// it with exponentiation by the precomputed alpha*beta. if not then
// at least this could be turned into a wNAF window table
let alpha_y = E::G1Affine::from(pvk.alpha_g1 * acc_Y);
ml_terms.push((&alpha_y, &pvk.beta_g2));

if E::multi_miller_loop(&ml_terms[..]).final_exponentiation() == E::Gt::identity() {
Ok(())
Expand All @@ -181,11 +187,11 @@ where
/// threadpool.
#[cfg(feature = "multicore")]
#[allow(non_snake_case)]
pub fn verify_multicore(self, vk: &VerifyingKey<E>) -> Result<(), VerificationError> {
pub fn verify_multicore(self, pvk: &PreparedVerifyingKey<E>) -> Result<(), VerificationError> {
if self
.items
.iter()
.any(|Item { inputs, .. }| inputs.len() + 1 != vk.ic.len())
.any(|Item { inputs, .. }| inputs.len() + 1 != pvk.ic.len())
{
return Err(VerificationError::InvalidVerifyingKey);
}
Expand All @@ -208,7 +214,7 @@ where
}
}

let ic_len = vk.ic.len();
let ic_len = pvk.ic.len();

let acc = self
.items
Expand All @@ -231,6 +237,7 @@ where
{
*acc_gamma_i += &(cur_z * a_i);
}
// TODO: could be done via multiexp across items
acc.delta += proof.c * cur_z;
acc.y += &cur_z;
ml_terms.push(((proof.a * cur_z).into(), (-proof.b).into()));
Expand Down Expand Up @@ -262,20 +269,20 @@ where
None => Ok(()),
Some(mut ml_result) => {
// TODO: could use a multiexp (Bos-Coster maybe?)
let psi = vk
let psi = pvk
.ic
.iter()
.zip(acc.gammas.into_iter())
.map(|(&psi_i, acc_gamma_i)| psi_i * acc_gamma_i)
.sum();

ml_result += E::multi_miller_loop(&[
(&acc.delta.to_affine(), &E::G2Prepared::from(vk.delta_g2)),
(&E::G1Affine::from(psi), &E::G2Prepared::from(vk.gamma_g2)),
(
&E::G1Affine::from(vk.alpha_g1 * acc.y),
&E::G2Prepared::from(vk.beta_g2),
),
(&acc.delta.to_affine(), &pvk.delta_g2),
(&E::G1Affine::from(psi), &pvk.gamma_g2),
// TODO: figure out of it's better to exponentiate
// precomputed alpha*beta by y here instead; otherwise
// consider using a window table
(&E::G1Affine::from(pvk.alpha_g1 * acc.y), &pvk.beta_g2),
]);

if ml_result.final_exponentiation() == E::Gt::identity() {
Expand Down
2 changes: 1 addition & 1 deletion tests/mimc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ fn batch_verify() {
let batch_start = Instant::now();

// Verify this batch for this specific verifying key
assert!(batch.verify(rng, &params.vk).is_ok());
assert!(batch.verify(rng, &pvk).is_ok());

batch_verifying += batch_start.elapsed();

Expand Down