diff --git a/Cargo.toml b/Cargo.toml index 1dfeb59..676ac26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ futures-cpupool = "0.1" num_cpus = "1" crossbeam = "0.3" pairing = "0.13" +byteorder = "1" [features] default = [] diff --git a/src/groth16/mod.rs b/src/groth16/mod.rs index 19cffc2..83e4099 100644 --- a/src/groth16/mod.rs +++ b/src/groth16/mod.rs @@ -1,6 +1,7 @@ use pairing::{ Engine, - CurveAffine + CurveAffine, + EncodedPoint }; use ::{ @@ -8,8 +9,9 @@ use ::{ }; use multiexp::SourceBuilder; - +use std::io::{self, Read, Write}; use std::sync::Arc; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; #[cfg(test)] mod tests; @@ -29,6 +31,72 @@ pub struct Proof { c: E::G1Affine } +impl PartialEq for Proof { + fn eq(&self, other: &Self) -> bool { + self.a == other.a && + self.b == other.b && + self.c == other.c + } +} + +impl Proof { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.a.into_compressed().as_ref())?; + writer.write_all(self.b.into_compressed().as_ref())?; + writer.write_all(self.c.into_compressed().as_ref())?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Compressed::empty(); + let mut g2_repr = ::Compressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let a = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g2_repr.as_mut())?; + let b = g2_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g1_repr.as_mut())?; + let c = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + Ok(Proof { + a: a, + b: b, + c: c + }) + } +} + #[derive(Clone)] pub struct VerifyingKey { // alpha in g1 for verifying and for creating A/C elements of @@ -57,6 +125,93 @@ pub struct VerifyingKey { ic: Vec } +impl PartialEq for VerifyingKey { + fn eq(&self, other: &Self) -> bool { + self.alpha_g1 == other.alpha_g1 && + self.beta_g1 == other.beta_g1 && + self.beta_g2 == other.beta_g2 && + self.gamma_g2 == other.gamma_g2 && + self.delta_g1 == other.delta_g1 && + self.delta_g2 == other.delta_g2 && + self.ic == other.ic + } +} + +impl VerifyingKey { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; + writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; + writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; + writer.write_u64::(self.ic.len() as u64)?; + for ic in &self.ic { + writer.write_all(ic.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Uncompressed::empty(); + let mut g2_repr = ::Uncompressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let beta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let delta_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let delta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let ic_len = reader.read_u64::()? as usize; + + let mut ic = vec![]; + + for _ in 0..ic_len { + reader.read_exact(g1_repr.as_mut())?; + let g1 = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + ic.push(g1); + } + + Ok(VerifyingKey { + alpha_g1: alpha_g1, + beta_g1: beta_g1, + beta_g2: beta_g2, + gamma_g2: gamma_g2, + delta_g1: delta_g1, + delta_g2: delta_g2, + ic: ic + }) + } +} + #[derive(Clone)] pub struct Parameters { pub vk: VerifyingKey, @@ -82,6 +237,150 @@ pub struct Parameters { b_g2: Arc> } +impl PartialEq for Parameters { + fn eq(&self, other: &Self) -> bool { + self.vk == other.vk && + self.h == other.h && + self.l == other.l && + self.a == other.a && + self.b_g1 == other.b_g1 && + self.b_g2 == other.b_g2 + } +} + +impl Parameters { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + self.vk.write(&mut writer)?; + + writer.write_u64::(self.h.len() as u64)?; + for g in &self.h[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u64::(self.l.len() as u64)?; + for g in &self.l[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u64::(self.a.len() as u64)?; + for g in &self.a[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u64::(self.b_g1.len() as u64)?; + for g in &self.b_g1[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + writer.write_u64::(self.b_g2.len() as u64)?; + for g in &self.b_g2[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R, + checked: bool + ) -> io::Result + { + let read_g1 = |reader: &mut R| -> io::Result { + let mut repr = ::Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + if checked { + repr + .into_affine() + } else { + repr + .into_affine_unchecked() + } + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + }) + }; + + let read_g2 = |reader: &mut R| -> io::Result { + let mut repr = ::Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + if checked { + repr + .into_affine() + } else { + repr + .into_affine_unchecked() + } + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + }) + }; + + let vk = VerifyingKey::::read(&mut reader)?; + + let mut h = vec![]; + let mut l = vec![]; + let mut a = vec![]; + let mut b_g1 = vec![]; + let mut b_g2 = vec![]; + + { + let len = reader.read_u64::()? as usize; + for _ in 0..len { + h.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u64::()? as usize; + for _ in 0..len { + l.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u64::()? as usize; + for _ in 0..len { + a.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u64::()? as usize; + for _ in 0..len { + b_g1.push(read_g1(&mut reader)?); + } + } + + { + let len = reader.read_u64::()? as usize; + for _ in 0..len { + b_g2.push(read_g2(&mut reader)?); + } + } + + Ok(Parameters { + vk: vk, + h: Arc::new(h), + l: Arc::new(l), + a: Arc::new(a), + b_g1: Arc::new(b_g1), + b_g2: Arc::new(b_g2) + }) + } +} + pub struct PreparedVerifyingKey { /// Pairing result of alpha*beta alpha_g1_beta_g2: E::Fqk, @@ -181,3 +480,97 @@ impl<'a, E: Engine> ParameterSource for &'a Parameters { Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) } } + +#[cfg(test)] +mod test_with_bls12_381 { + use super::*; + use {Circuit, SynthesisError, ConstraintSystem}; + + use rand::{Rand, thread_rng}; + use pairing::{Field}; + use pairing::bls12_381::{Bls12, Fr}; + + #[test] + fn serialization() { + struct MySillyCircuit { + a: Option, + b: Option + } + + impl Circuit for MySillyCircuit { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; + let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; + let c = cs.alloc_input(|| "c", || { + let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; + let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; + + a.mul_assign(&b); + Ok(a) + })?; + + cs.enforce( + || "a*b=c", + |lc| lc + a, + |lc| lc + b, + |lc| lc + c + ); + + Ok(()) + } + } + + let rng = &mut thread_rng(); + + let params = generate_random_parameters::( + MySillyCircuit { a: None, b: None }, + rng + ).unwrap(); + + { + let mut v = vec![]; + + params.write(&mut v).unwrap(); + assert_eq!(v.len(), 2160); + + let de_params = Parameters::read(&v[..], true).unwrap(); + assert!(params == de_params); + + let de_params = Parameters::read(&v[..], false).unwrap(); + assert!(params == de_params); + } + + let pvk = prepare_verifying_key::(¶ms.vk); + + for _ in 0..100 { + let a = Fr::rand(rng); + let b = Fr::rand(rng); + let mut c = a; + c.mul_assign(&b); + + let proof = create_random_proof( + MySillyCircuit { + a: Some(a), + b: Some(b) + }, + ¶ms, + rng + ).unwrap(); + + let mut v = vec![]; + proof.write(&mut v).unwrap(); + + assert_eq!(v.len(), 192); + + let de_proof = Proof::read(&v[..]).unwrap(); + assert!(proof == de_proof); + + assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); + assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index beec151..16c56db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate futures; extern crate futures_cpupool; extern crate bit_vec; extern crate crossbeam; +extern crate byteorder; pub mod multicore; pub mod multiexp;