use crossbeam_channel::{bounded, Receiver, Sender};
use ff::Field;
use group::{Curve, Group};
use pairing::{Engine, MillerLoopResult, MultiMillerLoop};
use rand_core::RngCore;
use rayon::prelude::*;
use crate::SynthesisError;
use std::ops::Mul;
use std::sync::{
atomic::{AtomicBool, Ordering::SeqCst},
Arc, Mutex,
};
use std::thread;
#[derive(Debug)]
pub struct PairingChecks<E, R>
where
E: MultiMillerLoop,
R: RngCore + Send,
{
valid: Arc<AtomicBool>,
merge_send: Sender<Result<PairingCheck<E>, SynthesisError>>,
valid_recv: Receiver<Result<bool, SynthesisError>>,
rng: Mutex<R>,
non_random_check_done: AtomicBool,
}
impl<E, R> PairingChecks<E, R>
where
E: MultiMillerLoop,
R: RngCore + Send,
{
#[allow(clippy::type_complexity)]
pub fn new(rng: R) -> Self {
let (merge_send, merge_recv): (
Sender<Result<PairingCheck<E>, SynthesisError>>,
Receiver<Result<PairingCheck<E>, SynthesisError>>,
) = bounded(10);
let (valid_send, valid_recv) = bounded(1);
let valid = Arc::new(AtomicBool::new(true));
let valid_copy = valid.clone();
thread::spawn(move || {
let mut acc = PairingCheck::new();
while let Ok(tuple) = merge_recv.recv() {
match tuple {
Ok(check) => {
if valid_copy.load(SeqCst) {
acc.merge(&check);
} else {
return;
}
}
Err(e) => {
valid_copy.store(false, SeqCst);
valid_send.send(Err(e)).expect("failed to send error");
return;
}
}
}
if valid_copy.load(SeqCst) {
valid_send.send(Ok(acc.verify())).expect("failed to send");
}
});
PairingChecks {
valid,
merge_send,
valid_recv,
rng: Mutex::new(rng),
non_random_check_done: AtomicBool::new(false),
}
}
pub fn invalidate(&self) {
self.valid.store(false, SeqCst);
}
pub fn report_err(&self, e: SynthesisError) {
self.merge_send
.send(Err(e))
.expect("expect to send on channel");
}
fn merge_pair(
&self,
result: <E as MultiMillerLoop>::Result,
exp: <E as Engine>::Gt,
must_randomize: bool,
) {
self.merge(PairingCheck::from_pair(result, exp), must_randomize);
}
fn merge_miller_one(&self, result: <E as MultiMillerLoop>::Result, must_randomize: bool) {
self.merge(PairingCheck::from_miller_one(result), must_randomize);
}
pub fn merge_nonrandom(
&self,
left: Vec<<E as MultiMillerLoop>::Result>,
right: <E as Engine>::Gt,
) {
let randomize = self.non_random_check_done.load(SeqCst);
self.merge_pair(left[0], right, randomize);
for l in left[1..].iter() {
self.merge_miller_one(*l, randomize);
}
}
pub fn merge_miller_inputs<'a>(
&self,
it: &[(&'a E::G1Affine, &'a E::G2Affine)],
out: &'a <E as Engine>::Gt,
) {
let must_randomize = self.non_random_check_done.load(SeqCst);
let coeff = {
let rng: &mut R = &mut self.rng.lock().unwrap();
derive_non_zero::<E, _>(rng)
};
self.merge(
PairingCheck::new_random_from_miller_inputs(coeff, it, out),
must_randomize,
);
}
fn merge(&self, check: PairingCheck<E>, must_randomize: bool) {
if !check.randomized {
assert!(
!must_randomize,
"Cannot merge non-randomized check with must_randomize true."
);
self.non_random_check_done.store(true, SeqCst);
};
let sent = self.merge_send.send(Ok(check));
if sent.is_err() && self.valid.load(SeqCst) {
panic!("Channel was closed although it is still valid.")
}
}
pub fn verify(self) -> Result<bool, SynthesisError> {
let Self {
valid,
merge_send,
valid_recv,
..
} = self;
drop(merge_send);
if !valid.load(SeqCst) {
return Ok(false);
}
valid_recv.recv().unwrap()
}
}
#[derive(Debug)]
struct PairingCheck<E>
where
E: MultiMillerLoop,
{
left: <E as MultiMillerLoop>::Result,
right: <E as Engine>::Gt,
randomized: bool,
}
impl<E> PairingCheck<E>
where
E: MultiMillerLoop,
{
fn new() -> PairingCheck<E> {
Self {
left: <E as MultiMillerLoop>::Result::default(),
right: <E as Engine>::Gt::generator(),
randomized: false,
}
}
fn from_pair(
result: <E as MultiMillerLoop>::Result,
exp: <E as Engine>::Gt,
) -> PairingCheck<E> {
Self {
left: result,
right: exp,
randomized: false,
}
}
fn from_miller_one(result: <E as MultiMillerLoop>::Result) -> PairingCheck<E> {
Self {
left: result,
right: <E as Engine>::Gt::generator(),
randomized: false,
}
}
pub fn new_random_from_miller_inputs<'a>(
coeff: E::Fr,
it: &[(&'a E::G1Affine, &'a E::G2Affine)],
out: &'a <E as Engine>::Gt,
) -> PairingCheck<E> {
let miller_out = it
.into_par_iter()
.map(|(a, b)| {
let na = a.mul(coeff).to_affine();
(na, (**b).into())
})
.map(|(a, b)| E::multi_miller_loop(&[(&a, &b)]))
.fold(<E as MultiMillerLoop>::Result::default, |acc, res| {
acc + res
})
.reduce(<E as MultiMillerLoop>::Result::default, |acc, res| {
acc + res
});
let right = if out != &<E as Engine>::Gt::generator() {
*out * coeff
} else {
*out
};
PairingCheck {
left: miller_out,
right,
randomized: true,
}
}
pub fn merge(&mut self, p2: &PairingCheck<E>) {
self.left += &p2.left;
add_if_not_one_gt::<E>(&mut self.right, &p2.right);
self.randomized = self.randomized && p2.randomized;
}
fn verify(&self) -> bool {
let left = self.left.final_exponentiation();
left == self.right
}
}
fn add_if_not_one_gt<E: Engine>(left: &mut E::Gt, right: &E::Gt) {
let one = E::Gt::generator();
if left == &one {
*left = *right;
return;
} else if right == &one {
return;
}
*left += right
}
fn derive_non_zero<E: Engine, R: rand_core::RngCore>(rng: &mut R) -> E::Fr {
loop {
let coeff = E::Fr::random(&mut *rng);
if coeff != E::Fr::zero() {
return coeff;
}
}
}
#[cfg(test)]
mod test {
use super::*;
use blstrs::{Bls12, G1Projective, G2Projective};
use group::Group;
use rand_core::RngCore;
use rand_core::SeedableRng;
fn gen_pairing_check<R: RngCore>(r: &mut R) -> PairingCheck<Bls12> {
let g1r = G1Projective::random(&mut *r).to_affine();
let g2r = G2Projective::random(&mut *r).to_affine();
let exp = Bls12::pairing(&g1r, &g2r);
let coeff = derive_non_zero::<Bls12, _>(r);
let tuple =
PairingCheck::<Bls12>::new_random_from_miller_inputs(coeff, &[(&g1r, &g2r)], &exp);
assert!(tuple.verify());
tuple
}
#[test]
fn test_pairing_randomize() {
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(0u64);
let tuples = (0..3)
.map(|_| gen_pairing_check(&mut rng))
.collect::<Vec<_>>();
let final_tuple = tuples
.iter()
.fold(PairingCheck::<Bls12>::new(), |mut acc, tu| {
acc.merge(tu);
acc
});
assert!(final_tuple.verify());
}
}