use ff::{Field, PrimeField};
use group::{Curve, Group};
use rand_core::SeedableRng;
use rand_xorshift::XorShiftRng;
mod dummy_engine;
use self::dummy_engine::*;
use std::marker::PhantomData;
use std::ops::{AddAssign, Mul, MulAssign, SubAssign};
use super::{
create_proof, create_proof_batch, generate_parameters, prepare_verifying_key, verify_proof,
};
use crate::{Circuit, ConstraintSystem, SynthesisError};
#[derive(Clone)]
struct XorDemo<Scalar: PrimeField> {
a: Option<bool>,
b: Option<bool>,
_marker: PhantomData<Scalar>,
}
impl<Scalar: PrimeField> Circuit<Scalar> for XorDemo<Scalar> {
fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
let a_var = cs.alloc(
|| "a",
|| {
if self.a.is_some() {
if self.a.unwrap() {
Ok(Scalar::one())
} else {
Ok(Scalar::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
},
)?;
cs.enforce(
|| "a_boolean_constraint",
|lc| lc + CS::one() - a_var,
|lc| lc + a_var,
|lc| lc,
);
let b_var = cs.alloc(
|| "b",
|| {
if self.b.is_some() {
if self.b.unwrap() {
Ok(Scalar::one())
} else {
Ok(Scalar::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
},
)?;
cs.enforce(
|| "b_boolean_constraint",
|lc| lc + CS::one() - b_var,
|lc| lc + b_var,
|lc| lc,
);
let c_var = cs.alloc_input(
|| "c",
|| {
if self.a.is_some() && self.b.is_some() {
if self.a.unwrap() ^ self.b.unwrap() {
Ok(Scalar::one())
} else {
Ok(Scalar::zero())
}
} else {
Err(SynthesisError::AssignmentMissing)
}
},
)?;
cs.enforce(
|| "c_xor_constraint",
|lc| lc + a_var + a_var,
|lc| lc + b_var,
|lc| lc + a_var + b_var - c_var,
);
Ok(())
}
}
#[test]
fn test_xordemo() {
let g1 = Fr::one();
let g2 = Fr::one();
let alpha = Fr::from(48577u64);
let beta = Fr::from(22580u64);
let gamma = Fr::from(53332u64);
let delta = Fr::from(5481u64);
let tau = Fr::from(3673u64);
let params = {
let c = XorDemo::<Fr> {
a: None,
b: None,
_marker: PhantomData,
};
generate_parameters(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
};
assert_eq!(7, params.h.len());
let mut root_of_unity = Fr::root_of_unity();
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 10]));
root_of_unity = root_of_unity.pow_vartime(&[1 << 7]);
assert_eq!(Fr::one(), root_of_unity.pow_vartime(&[1 << 3]));
assert_eq!(Fr::from(20201u64), root_of_unity);
let mut points = Vec::with_capacity(8);
for i in 0..8 {
points.push(root_of_unity.pow_vartime(&[i]));
}
let mut t_at_tau = tau.pow_vartime(&[8]);
t_at_tau.sub_assign(&Fr::one());
{
let mut tmp = Fr::one();
for p in &points {
let mut term = tau;
term.sub_assign(p);
tmp.mul_assign(&term);
}
assert_eq!(tmp, t_at_tau);
}
let delta_inverse = delta.invert().unwrap();
let gamma_inverse = gamma.invert().unwrap();
{
let mut coeff = delta_inverse;
coeff.mul_assign(&t_at_tau);
let mut cur = Fr::one();
for h in params.h.iter() {
let mut tmp = cur;
tmp.mul_assign(&coeff);
assert_eq!(*h, tmp);
cur.mul_assign(&tau);
}
}
assert_eq!(2, params.vk.ic.len());
assert_eq!(2, params.l.len());
assert_eq!(4, params.a.len());
assert_eq!(2, params.b_g1.len());
assert_eq!(2, params.b_g2.len());
let u_i = [59158, 48317, 21767, 10402]
.iter()
.map(|e| Fr::from_str_vartime(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
let v_i = [0, 0, 60619, 30791]
.iter()
.map(|e| Fr::from_str_vartime(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
let w_i = [0, 23320, 41193, 41193]
.iter()
.map(|e| Fr::from_str_vartime(&format!("{}", e)).unwrap())
.collect::<Vec<Fr>>();
for (u, a) in u_i.iter().zip(¶ms.a[..]) {
assert_eq!(u, a);
}
for (v, b) in v_i
.iter()
.filter(|&&e| e != Fr::zero())
.zip(¶ms.b_g1[..])
{
assert_eq!(v, b);
}
for (v, b) in v_i
.iter()
.filter(|&&e| e != Fr::zero())
.zip(¶ms.b_g2[..])
{
assert_eq!(v, b);
}
for i in 0..4 {
let mut tmp1 = beta;
tmp1.mul_assign(&u_i[i]);
let mut tmp2 = alpha;
tmp2.mul_assign(&v_i[i]);
tmp1.add_assign(&tmp2);
tmp1.add_assign(&w_i[i]);
if i < 2 {
tmp1.mul_assign(&gamma_inverse);
assert_eq!(tmp1, params.vk.ic[i]);
} else {
tmp1.mul_assign(&delta_inverse);
assert_eq!(tmp1, params.l[i - 2]);
}
}
assert_eq!(alpha, params.vk.alpha_g1);
assert_eq!(beta, params.vk.beta_g1);
assert_eq!(beta, params.vk.beta_g2);
assert_eq!(gamma, params.vk.gamma_g2);
assert_eq!(delta, params.vk.delta_g1);
assert_eq!(delta, params.vk.delta_g2);
let pvk = prepare_verifying_key(¶ms.vk);
let r = Fr::from(27134u64);
let s = Fr::from(17146u64);
let proof = {
let c = XorDemo {
a: Some(true),
b: Some(false),
_marker: PhantomData,
};
create_proof::<DummyEngine, _, _>(c, ¶ms, r, s).unwrap()
};
{
let mut expected_a = delta;
expected_a.mul_assign(&r);
expected_a.add_assign(&alpha);
expected_a.add_assign(&u_i[0]); expected_a.add_assign(&u_i[1]); expected_a.add_assign(&u_i[2]); assert_eq!(proof.a, expected_a);
}
{
let mut expected_b = delta;
expected_b.mul_assign(&s);
expected_b.add_assign(&beta);
expected_b.add_assign(&v_i[0]); expected_b.add_assign(&v_i[1]); expected_b.add_assign(&v_i[2]); assert_eq!(proof.b, expected_b);
}
{
let mut expected_c = Fr::zero();
let mut tmp = proof.a;
tmp.mul_assign(&s);
expected_c.add_assign(&tmp);
let mut tmp = proof.b;
tmp.mul_assign(&r);
expected_c.add_assign(&tmp);
let mut tmp = delta;
tmp.mul_assign(&r);
tmp.mul_assign(&s);
expected_c.sub_assign(&tmp);
expected_c.add_assign(¶ms.l[0]);
for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739]
.iter()
.enumerate()
{
let coeff = Fr::from_str_vartime(&format!("{}", coeff)).unwrap();
let mut tmp = params.h[i];
tmp.mul_assign(&coeff);
expected_c.add_assign(&tmp);
}
assert_eq!(expected_c, proof.c);
}
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
}
#[test]
fn test_create_batch_single() {
let g1 = Fr::one();
let g2 = Fr::one();
let alpha = Fr::from(48577u64);
let beta = Fr::from(22580u64);
let gamma = Fr::from(53332u64);
let delta = Fr::from(5481u64);
let tau = Fr::from(3673u64);
let params = {
let c = XorDemo::<Fr> {
a: None,
b: None,
_marker: PhantomData,
};
generate_parameters::<DummyEngine, _>(c, g1, g2, alpha, beta, gamma, delta, tau).unwrap()
};
let pvk = prepare_verifying_key(¶ms.vk);
let r1 = Fr::from(27134u64);
let s1 = Fr::from(17146u64);
let r2 = Fr::from(27132u64);
let s2 = Fr::from(17142u64);
let c = XorDemo {
a: Some(true),
b: Some(false),
_marker: PhantomData,
};
let proof_single_1 = create_proof(c.clone(), ¶ms, r1, s1).unwrap();
let proof_single_2 = create_proof(c.clone(), ¶ms, r2, s2).unwrap();
let proof_batch =
create_proof_batch(vec![c.clone(), c], ¶ms, vec![r1, r2], vec![s1, s2]).unwrap();
assert_eq!(proof_batch[0], proof_single_1);
assert_eq!(proof_batch[1], proof_single_2);
assert!(verify_proof(&pvk, &proof_single_1, &[Fr::one()]).unwrap());
assert!(verify_proof(&pvk, &proof_single_2, &[Fr::one()]).unwrap());
for proof in &proof_batch {
assert!(verify_proof(&pvk, proof, &[Fr::one()]).unwrap());
}
}
#[test]
#[allow(clippy::manual_swap)]
fn test_verify_random_single() {
use crate::groth16::{create_random_proof, generate_random_parameters, Proof};
use blstrs::{Bls12, G1Projective, G2Projective, Scalar as Fr};
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let params = {
let c = XorDemo::<Fr> {
a: None,
b: None,
_marker: PhantomData,
};
generate_random_parameters::<Bls12, _, _>(c, &mut rng).unwrap()
};
let pvk = prepare_verifying_key(¶ms.vk);
for _ in 0..50 {
let c = XorDemo {
a: Some(true),
b: Some(false),
_marker: PhantomData,
};
let proof = create_random_proof(c.clone(), ¶ms, &mut rng).unwrap();
assert!(verify_proof(&pvk, &proof, &[Fr::one()]).unwrap());
{
assert!(!verify_proof(&pvk, &proof, &[Fr::random(&mut rng)]).unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof.a = fake_proof.a.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proof(&pvk, &fake_proof, &[Fr::one()]).unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof.b = fake_proof.b.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proof(&pvk, &fake_proof, &[Fr::one()]).unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof.c = fake_proof.c.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proof(&pvk, &fake_proof, &[Fr::one()]).unwrap());
}
{
let mut fake_proof = proof.clone();
std::mem::swap(&mut fake_proof.c, &mut fake_proof.a);
assert!(!verify_proof(&pvk, &fake_proof, &[Fr::one()]).unwrap());
}
{
let random_proof = Proof {
a: G1Projective::random(&mut rng).to_affine(),
b: G2Projective::random(&mut rng).to_affine(),
c: G1Projective::random(&mut rng).to_affine(),
};
assert!(!verify_proof(&pvk, &random_proof, &[Fr::one()]).unwrap());
}
}
}
#[test]
#[allow(clippy::manual_swap)]
fn test_verify_random_batch() {
use crate::groth16::{
create_random_proof_batch, generate_random_parameters, verify_proofs_batch, Proof,
};
use blstrs::{Bls12, G1Projective, G2Projective, Scalar as Fr};
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
0xe5,
]);
let params = {
let c = XorDemo::<Fr> {
a: None,
b: None,
_marker: PhantomData,
};
generate_random_parameters::<Bls12, _, _>(c, &mut rng).unwrap()
};
let pvk = prepare_verifying_key(¶ms.vk);
let inputs = vec![vec![Fr::one()], vec![Fr::one()], vec![Fr::one()]];
for _ in 0..50 {
let c = XorDemo {
a: Some(true),
b: Some(false),
_marker: PhantomData,
};
let proof =
create_random_proof_batch(vec![c.clone(), c.clone(), c.clone()], ¶ms, &mut rng)
.unwrap();
assert!(
verify_proofs_batch(&pvk, &mut rng, &[&proof[0], &proof[1], &proof[2]], &inputs)
.unwrap()
);
{
let r = Fr::random(&mut rng);
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&proof[0], &proof[1], &proof[2]],
&[vec![r], vec![Fr::one()], vec![Fr::one()]],
)
.unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof[0].a = fake_proof[0].a.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&fake_proof[0], &fake_proof[1], &fake_proof[2]],
&inputs
)
.unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof[1].b = fake_proof[1].b.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&fake_proof[0], &fake_proof[1], &fake_proof[2]],
&inputs
)
.unwrap());
}
{
let mut fake_proof = proof.clone();
fake_proof[2].c = fake_proof[2].c.mul(Fr::random(&mut rng)).to_affine();
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&fake_proof[0], &fake_proof[1], &fake_proof[2]],
&inputs
)
.unwrap());
}
{
let mut fake_proof = proof.clone();
let fp0 = &mut fake_proof[0];
std::mem::swap(&mut fp0.c, &mut fp0.a);
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&fake_proof[0], &fake_proof[1], &fake_proof[2]],
&inputs
)
.unwrap());
}
{
let random_proof = [
Proof {
a: G1Projective::random(&mut rng).to_affine(),
b: G2Projective::random(&mut rng).to_affine(),
c: G1Projective::random(&mut rng).to_affine(),
},
Proof {
a: G1Projective::random(&mut rng).to_affine(),
b: G2Projective::random(&mut rng).to_affine(),
c: G1Projective::random(&mut rng).to_affine(),
},
Proof {
a: G1Projective::random(&mut rng).to_affine(),
b: G2Projective::random(&mut rng).to_affine(),
c: G1Projective::random(&mut rng).to_affine(),
},
];
assert!(!verify_proofs_batch(
&pvk,
&mut rng,
&[&random_proof[0], &random_proof[1], &random_proof[2],],
&inputs
)
.unwrap());
}
}
}