extern crate bellman; extern crate pairing; extern crate rand; extern crate ff; // For randomness (during paramgen and proof generation) use rand::{thread_rng, Rng}; // For benchmarking use std::time::{Duration, Instant}; // Bring in some tools for using pairing-friendly curves use pairing::{ Engine }; use ff::{ Field, }; // We're going to use the BLS12-381 pairing-friendly elliptic curve. use pairing::bls12_381::{ Bls12 }; use pairing::bn256::{ Bn256 }; // We'll use these interfaces to construct our circuit. use bellman::{ Circuit, ConstraintSystem, SynthesisError }; // We're going to use the Groth16 proving system. use bellman::groth16::{ Proof, generate_random_parameters, prepare_verifying_key, create_random_proof, verify_proof, }; const MIMC_ROUNDS: usize = 322; /// This is an implementation of MiMC, specifically a /// variant named `LongsightF322p3` for BLS12-381. /// See http://eprint.iacr.org/2016/492 for more /// information about this construction. /// /// ``` /// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { /// for i from 0 up to 321 { /// xL, xR := xR + (xL + Ci)^3, xL /// } /// return xL /// } /// ``` fn mimc( mut xl: E::Fr, mut xr: E::Fr, constants: &[E::Fr] ) -> E::Fr { assert_eq!(constants.len(), MIMC_ROUNDS); for i in 0..MIMC_ROUNDS { let mut tmp1 = xl; tmp1.add_assign(&constants[i]); let mut tmp2 = tmp1; tmp2.square(); tmp2.mul_assign(&tmp1); tmp2.add_assign(&xr); xr = xl; xl = tmp2; } xl } /// This is our demo circuit for proving knowledge of the /// preimage of a MiMC hash invocation. #[derive(Clone)] struct MiMCDemo<'a, E: Engine> { xl: Option, xr: Option, constants: &'a [E::Fr] } /// Our demo circuit implements this `Circuit` trait which /// is used during paramgen and proving in order to /// synthesize the constraint system. impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { fn synthesize>( self, cs: &mut CS ) -> Result<(), SynthesisError> { assert_eq!(self.constants.len(), MIMC_ROUNDS); // Allocate the first component of the preimage. let mut xl_value = self.xl; let mut xl = cs.alloc(|| "preimage xl", || { xl_value.ok_or(SynthesisError::AssignmentMissing) })?; // Allocate the second component of the preimage. let mut xr_value = self.xr; let mut xr = cs.alloc(|| "preimage xr", || { xr_value.ok_or(SynthesisError::AssignmentMissing) })?; for i in 0..MIMC_ROUNDS { // xL, xR := xR + (xL + Ci)^3, xL let cs = &mut cs.namespace(|| format!("round {}", i)); // tmp = (xL + Ci)^2 let mut tmp_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.square(); e }); let mut tmp = cs.alloc(|| "tmp", || { tmp_value.ok_or(SynthesisError::AssignmentMissing) })?; cs.enforce( || "tmp = (xL + Ci)^2", |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + tmp ); // new_xL = xR + (xL + Ci)^3 // new_xL = xR + tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci) let mut new_xl_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.mul_assign(&tmp_value.unwrap()); e.add_assign(&xr_value.unwrap()); e }); let mut new_xl = if i == (MIMC_ROUNDS-1) { // This is the last round, xL is our image and so // we allocate a public input. cs.alloc_input(|| "image", || { new_xl_value.ok_or(SynthesisError::AssignmentMissing) })? } else { cs.alloc(|| "new_xl", || { new_xl_value.ok_or(SynthesisError::AssignmentMissing) })? }; cs.enforce( || "new_xL = xR + (xL + Ci)^3", |lc| lc + tmp, |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + new_xl - xr ); // xR = xL xr = xl; xr_value = xl_value; // xL = new_xL xl = new_xl; xl_value = new_xl_value; } Ok(()) } } #[test] fn test_mimc() { // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); println!("Creating parameters..."); // Create parameters for our circuit let params = { let c = MiMCDemo:: { xl: None, xr: None, constants: &constants }; generate_random_parameters(c, rng).unwrap() }; // Prepare the verification key (for proof verification) let pvk = prepare_verifying_key(¶ms.vk); println!("Creating proofs..."); // Let's benchmark stuff! const SAMPLES: u32 = 50; let mut total_proving = Duration::new(0, 0); let mut total_verifying = Duration::new(0, 0); // Just a place to put the proof data, so we can // benchmark deserialization. let mut proof_vec = vec![]; for _ in 0..SAMPLES { // Generate a random preimage and compute the image let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); proof_vec.truncate(0); let start = Instant::now(); { // Create an instance of our circuit (with the // witness) let c = MiMCDemo { xl: Some(xl), xr: Some(xr), constants: &constants }; // Create a groth16 proof with our parameters. let proof = create_random_proof(c, ¶ms, rng).unwrap(); proof.write(&mut proof_vec).unwrap(); } total_proving += start.elapsed(); let start = Instant::now(); let proof = Proof::read(&proof_vec[..]).unwrap(); // Check the proof assert!(verify_proof( &pvk, &proof, &[image] ).unwrap()); total_verifying += start.elapsed(); } let proving_avg = total_proving / SAMPLES; let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); let verifying_avg = total_verifying / SAMPLES; let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); println!("Average proving time: {:?} seconds", proving_avg); println!("Average verifying time: {:?} seconds", verifying_avg); } #[test] fn test_mimc_bn256() { // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); println!("Creating parameters..."); // Create parameters for our circuit let params = { let c = MiMCDemo:: { xl: None, xr: None, constants: &constants }; generate_random_parameters(c, rng).unwrap() }; // Prepare the verification key (for proof verification) let pvk = prepare_verifying_key(¶ms.vk); println!("Creating proofs..."); // Let's benchmark stuff! const SAMPLES: u32 = 50; let mut total_proving = Duration::new(0, 0); let mut total_verifying = Duration::new(0, 0); // Just a place to put the proof data, so we can // benchmark deserialization. let mut proof_vec = vec![]; for _ in 0..SAMPLES { // Generate a random preimage and compute the image let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); proof_vec.truncate(0); let start = Instant::now(); { // Create an instance of our circuit (with the // witness) let c = MiMCDemo { xl: Some(xl), xr: Some(xr), constants: &constants }; // Create a groth16 proof with our parameters. let proof = create_random_proof(c, ¶ms, rng).unwrap(); proof.write(&mut proof_vec).unwrap(); } total_proving += start.elapsed(); let start = Instant::now(); let proof = Proof::read(&proof_vec[..]).unwrap(); // Check the proof assert!(verify_proof( &pvk, &proof, &[image] ).unwrap()); total_verifying += start.elapsed(); } let proving_avg = total_proving / SAMPLES; let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); let verifying_avg = total_verifying / SAMPLES; let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); println!("Average proving time: {:?} seconds", proving_avg); println!("Average verifying time: {:?} seconds", verifying_avg); } /// This is our demo circuit for proving knowledge of the /// preimage of a MiMC hash invocation. #[derive(Clone)] struct MiMCDemoNoInputs<'a, E: Engine> { xl: Option, xr: Option, image: Option, constants: &'a [E::Fr] } /// Our demo circuit implements this `Circuit` trait which /// is used during paramgen and proving in order to /// synthesize the constraint system. impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { fn synthesize>( self, cs: &mut CS ) -> Result<(), SynthesisError> { assert_eq!(self.constants.len(), MIMC_ROUNDS); // Allocate the first component of the preimage. let mut xl_value = self.xl; let mut xl = cs.alloc(|| "preimage xl", || { xl_value.ok_or(SynthesisError::AssignmentMissing) })?; // Allocate the second component of the preimage. let mut xr_value = self.xr; let mut xr = cs.alloc(|| "preimage xr", || { xr_value.ok_or(SynthesisError::AssignmentMissing) })?; for i in 0..MIMC_ROUNDS { // xL, xR := xR + (xL + Ci)^3, xL let cs = &mut cs.namespace(|| format!("round {}", i)); // tmp = (xL + Ci)^2 let mut tmp_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.square(); e }); let mut tmp = cs.alloc(|| "tmp", || { tmp_value.ok_or(SynthesisError::AssignmentMissing) })?; cs.enforce( || "tmp = (xL + Ci)^2", |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + tmp ); // new_xL = xR + (xL + Ci)^3 // new_xL = xR + tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci) let mut new_xl_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.mul_assign(&tmp_value.unwrap()); e.add_assign(&xr_value.unwrap()); e }); let mut new_xl = if i == (MIMC_ROUNDS-1) { // This is the last round, xL is our image and so // we use the image let image_value = self.image; cs.alloc(|| "image", || { image_value.ok_or(SynthesisError::AssignmentMissing) })? } else { cs.alloc(|| "new_xl", || { new_xl_value.ok_or(SynthesisError::AssignmentMissing) })? }; cs.enforce( || "new_xL = xR + (xL + Ci)^3", |lc| lc + tmp, |lc| lc + xl + (self.constants[i], CS::one()), |lc| lc + new_xl - xr ); // xR = xL xr = xl; xr_value = xl_value; // xL = new_xL xl = new_xl; xl_value = new_xl_value; } Ok(()) } } #[test] fn test_sonic_mimc() { use ff::{Field, PrimeField}; use pairing::{Engine, CurveAffine, CurveProjective}; use pairing::bls12_381::{Bls12, Fr}; use std::time::{Instant}; use bellman::sonic::srs::SRS; let srs_x = Fr::from_str("23923").unwrap(); let srs_alpha = Fr::from_str("23728792").unwrap(); println!("making srs"); let start = Instant::now(); let srs = SRS::::dummy(830564, srs_x, srs_alpha); println!("done in {:?}", start.elapsed()); { // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); let samples: usize = 100; let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); // Create an instance of our circuit (with the // witness) let circuit = MiMCDemoNoInputs { xl: Some(xl), xr: Some(xr), image: Some(image), constants: &constants }; use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; use bellman::sonic::helped::helper::{create_aggregate_on_srs}; println!("creating proof"); let start = Instant::now(); let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); println!("done in {:?}", start.elapsed()); { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 1 proof without advice"); let start = Instant::now(); { for _ in 0..1 { verifier.add_proof(&proof, &[], |_, _| None); } assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying {} proofs without advice", samples); let start = Instant::now(); { for _ in 0..samples { verifier.add_proof(&proof, &[], |_, _| None); } assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 100 proofs with advice"); let start = Instant::now(); { for (ref proof, ref advice) in &proofs { verifier.add_proof_with_advice(proof, &[], advice); } verifier.add_aggregate(&proofs, &aggregate); assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } } } #[test] fn test_inputs_into_sonic_mimc() { use ff::{Field, PrimeField}; use pairing::{Engine, CurveAffine, CurveProjective}; use pairing::bn256::{Bn256, Fr}; // use pairing::bls12_381::{Bls12, Fr}; use std::time::{Instant}; use bellman::sonic::srs::SRS; let srs_x = Fr::from_str("23923").unwrap(); let srs_alpha = Fr::from_str("23728792").unwrap(); println!("making srs"); let start = Instant::now(); let srs = SRS::::dummy(830564, srs_x, srs_alpha); println!("done in {:?}", start.elapsed()); { // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); let samples: usize = 100; let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); // Create an instance of our circuit (with the // witness) let circuit = MiMCDemo { xl: Some(xl), xr: Some(xr), constants: &constants }; use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; use bellman::sonic::helped::helper::{create_aggregate_on_srs}; let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); println!("{:?}", info); println!("creating proof"); let start = Instant::now(); let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); println!("done in {:?}", start.elapsed()); { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 1 proof without advice"); let start = Instant::now(); { for _ in 0..1 { verifier.add_proof(&proof, &[image], |_, _| None); } assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying {} proofs without advice", samples); let start = Instant::now(); { for _ in 0..samples { verifier.add_proof(&proof, &[image], |_, _| None); } assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } { let rng = thread_rng(); let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 100 proofs with advice and aggregate"); let start = Instant::now(); { for (ref proof, ref advice) in &proofs { verifier.add_proof_with_advice(proof, &[image], advice); } verifier.add_aggregate(&proofs, &aggregate); assert_eq!(verifier.check_all(), true); // TODO } println!("done in {:?}", start.elapsed()); } } } #[test] fn test_high_level_sonic_api() { use pairing::bn256::{Bn256}; use std::time::{Instant}; use bellman::sonic::helped::{ generate_random_parameters, verify_aggregate, verify_proofs, create_proof, create_advice, create_aggregate }; { // This may not be cryptographically safe, use // `OsRng` (for example) in production software. let mut rng = &mut thread_rng(); // Generate the MiMC round constants let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); let samples: usize = 100; let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); // Create an instance of our circuit (with the // witness) let circuit = MiMCDemo { xl: Some(xl), xr: Some(xr), constants: &constants }; let params = generate_random_parameters(circuit.clone(), &mut rng).unwrap(); println!("creating proof"); let start = Instant::now(); let proof = create_proof(circuit.clone(), ¶ms).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); let advice = create_advice(circuit.clone(), &proof, ¶ms).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); let aggregate = create_aggregate::(circuit.clone(), &proofs, ¶ms); println!("done in {:?}", start.elapsed()); { println!("verifying 1 proof without advice"); let rng = thread_rng(); let start = Instant::now(); assert_eq!(verify_proofs(&vec![proof.clone()], &vec![vec![image.clone()]], circuit.clone(), rng, ¶ms).unwrap(), true); println!("done in {:?}", start.elapsed()); } { println!("verifying {} proofs without advice", samples); let rng = thread_rng(); let start = Instant::now(); assert_eq!(verify_proofs(&vec![proof.clone(); 100], &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); println!("done in {:?}", start.elapsed()); } { println!("verifying 100 proofs with advice and aggregate"); let rng = thread_rng(); let start = Instant::now(); assert_eq!(verify_aggregate(&vec![(proof.clone(), advice.clone()); 100], &aggregate, &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); println!("done in {:?}", start.elapsed()); } } }