extern crate ff;
extern crate rand;
mod util;
use ff::Field;
use blstrs::Bls12;
use pairing::Engine;
use bellperson::groth16::{
Parameters, Proof, aggregate::AggregateVersion, create_random_proof,
generate_random_parameters, prepare_verifying_key, verify_proof,
};
use bellperson::{Circuit, ConstraintSystem, SynthesisError};
pub const MINROOT_ROUNDS: usize = 10;
pub fn minroot<E: Engine>(mut xl: E::Fr, mut xr: E::Fr) -> (E::Fr, E::Fr) {
for _ in 0..MINROOT_ROUNDS {
let mut tmp1 = xl;
tmp1 += xr;
let tmp2 = tmp1.pow_vartime([
0x33333332CCCCCCCD,
0x217F0E679998F199,
0xE14A56699D73F002,
0x2E5F0FBADD72321C,
]);
xr = xl;
xl = tmp2;
}
(xl, xr)
}
fn fifth_root<E: Engine>(x: E::Fr) -> Option<E::Fr> {
Some(x.pow_vartime([
0x33333332CCCCCCCD,
0x217F0E679998F199,
0xE14A56699D73F002,
0x2E5F0FBADD72321C,
]))
}
#[derive(Clone)]
pub struct MinRoot<E: Engine> {
pub xl: Option<E::Fr>,
pub xr: Option<E::Fr>,
}
impl<E: Engine> Circuit<E::Fr> for MinRoot<E> {
fn synthesize<CS: ConstraintSystem<E::Fr>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
let mut xl_value = self.xl;
let mut xl = cs.alloc_input(
|| "preimage xl",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?;
let mut xr_value = self.xr;
let mut xr = cs.alloc_input(
|| "preimage xr",
|| xr_value.ok_or(SynthesisError::AssignmentMissing),
)?;
for i in 0..MINROOT_ROUNDS {
let cs = &mut cs.namespace(|| format!("round {}", i));
let mut new_xl_value = None;
if let Some(xl_value) = xl_value {
let mut tmp1 = xl_value;
tmp1 += &xr_value.unwrap();
new_xl_value = fifth_root::<E>(tmp1);
}
let new_xl = if i == (MINROOT_ROUNDS - 1) {
cs.alloc_input(
|| "image_xl",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else {
cs.alloc(
|| "new_xl",
|| new_xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
};
let tmp2 = new_xl_value.map(|mut e| {
e = e.square();
e
});
let tmp3 = tmp2.map(|mut e| {
e = e.square();
e
});
let tmp2 = cs.alloc(|| "tmp2", || tmp2.ok_or(SynthesisError::AssignmentMissing))?;
let tmp3 = cs.alloc(|| "tmp3", || tmp3.ok_or(SynthesisError::AssignmentMissing))?;
let new_xr = if i == (MINROOT_ROUNDS - 1) {
cs.alloc_input(
|| "image_xr",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
} else {
cs.alloc(
|| "new_xr",
|| xl_value.ok_or(SynthesisError::AssignmentMissing),
)?
};
cs.enforce(
|| "tmp2 = new_xl^2",
|lc| lc + new_xl,
|lc| lc + new_xl,
|lc| lc + tmp2,
);
cs.enforce(
|| "tmp3 = tmp2^2",
|lc| lc + tmp2,
|lc| lc + tmp2,
|lc| lc + tmp3,
);
cs.enforce(
|| "new_xL^5 = xl + xr",
|lc| lc + tmp3,
|lc| lc + new_xl,
|lc| lc + xl + xr,
);
xr = new_xr;
xr_value = xl_value;
xl = new_xl;
xl_value = new_xl_value;
}
Ok(())
}
}
#[test]
fn minroot_test() {
let rng = &mut rand_core::OsRng;
let params: Parameters<Bls12> = {
let c = MinRoot::<Bls12> { xl: None, xr: None };
generate_random_parameters(c, rng).unwrap()
};
let pvk = prepare_verifying_key(¶ms.vk);
#[cfg(feature = "cuda-supraseal")]
let params = util::supraseal::supraseal_params(params);
let mut proof_vec = vec![];
let mut proofs = vec![];
let mut images = vec![];
let xl = <Bls12 as Engine>::Fr::random(*rng);
let xr = <Bls12 as Engine>::Fr::random(*rng);
let (image_xl, image_xr) = minroot::<Bls12>(xl, xr);
proof_vec.truncate(0);
{
let c = MinRoot::<Bls12> {
xl: Some(xl),
xr: Some(xr),
};
let proof = create_random_proof(c, ¶ms, rng).unwrap();
proof.write(&mut proof_vec).unwrap();
}
let proof = Proof::read(&proof_vec[..]).unwrap();
assert!(verify_proof(&pvk, &proof, &[xl, xr, image_xl, image_xr]).unwrap());
proofs.push(proof);
images.push(vec![xl, xr, image_xl, image_xr]);
}
use bellperson::groth16::aggregate::{
GenericSRS, aggregate_proofs_and_instances, setup_fake_srs,
verify_aggregate_proof_and_aggregate_instances,
};
use blstrs::Scalar as Fr;
use rand_core::SeedableRng;
#[test]
fn minroot_aggregate_proof() {
minroot_aggregate_proof_inner(AggregateVersion::V1);
minroot_aggregate_proof_inner(AggregateVersion::V2);
}
fn minroot_aggregate_proof_inner(version: AggregateVersion) {
let nb_proofs: usize = 128;
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(0u64);
let generic: GenericSRS<Bls12> = setup_fake_srs(&mut rng, nb_proofs);
let params = {
let c = MinRoot::<Bls12> { xl: None, xr: None };
generate_random_parameters(c, &mut rng).unwrap()
};
let pvk = prepare_verifying_key(¶ms.vk);
#[cfg(feature = "cuda-supraseal")]
let params = util::supraseal::supraseal_params(params);
let mut xl = <Bls12 as Engine>::Fr::random(&mut rng);
let mut xr = <Bls12 as Engine>::Fr::random(&mut rng);
let public_inputs = [xl, xr].to_vec();
let mut proofs: Vec<Proof<Bls12>> = Vec::new();
let mut statements: Vec<Vec<Fr>> = Vec::new();
let mut statement_circuit: (Vec<Fr>, MinRoot<Bls12>);
for _ in 0..nb_proofs {
statement_circuit = generate_proof(xl, xr);
xl = statement_circuit.0[2];
xr = statement_circuit.0[3];
let proof = create_random_proof(statement_circuit.1, ¶ms, &mut rng).unwrap();
assert!(verify_proof(&pvk, &proof, &statement_circuit.0).unwrap());
proofs.push(proof);
statements.push(statement_circuit.0);
}
let public_outputs = [xl, xr].to_vec();
let mut buf = Vec::new();
proofs[0].write(&mut buf).expect("buffer");
let inclusion = vec![1, 2, 3];
env_logger::try_init().ok();
let (pk, vk) = generic.specialize_input_aggregation(nb_proofs);
let aggregate_proof_and_instance = aggregate_proofs_and_instances::<Bls12>(
&pk,
&inclusion,
&statements[..nb_proofs],
&proofs[..nb_proofs],
version,
)
.expect("failed to aggregate proofs");
let verified = verify_aggregate_proof_and_aggregate_instances(
&vk,
&pvk,
&mut rng,
&public_inputs,
&public_outputs,
&aggregate_proof_and_instance,
&inclusion,
version,
)
.unwrap();
assert!(verified, "failed to verify aggregate proof");
}
fn generate_proof(
xl_old: <Bls12 as Engine>::Fr,
xr_old: <Bls12 as Engine>::Fr,
) -> (Vec<Fr>, MinRoot<Bls12>) {
let (image_xl, image_xr) = minroot::<Bls12>(xl_old, xr_old);
let c = MinRoot::<Bls12> {
xl: Some(xl_old),
xr: Some(xr_old),
};
(vec![xl_old, xr_old, image_xl, image_xr], c)
}