From baa3cb9b3b3cd80dba84ba9d6b61ddb41c9c26c5 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 11:41:45 +0100 Subject: [PATCH 01/40] refactor: move phase2 binary to filecoin-phase2 crate --- Cargo.toml | 3 +- filecoin-proofs/Cargo.toml | 13 +++------ phase2/Cargo.toml | 29 +++++++++++++++++++ .../src/bin/phase2.rs => phase2/src/main.rs | 0 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 phase2/Cargo.toml rename filecoin-proofs/src/bin/phase2.rs => phase2/src/main.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 0a0b6acae..c46403716 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ "storage-proofs/porep", "storage-proofs/post", "fil-proofs-tooling", - "sha2raw" + "sha2raw", + "phase2", ] diff --git a/filecoin-proofs/Cargo.toml b/filecoin-proofs/Cargo.toml index dd3593e1b..ba90f2ca7 100644 --- a/filecoin-proofs/Cargo.toml +++ b/filecoin-proofs/Cargo.toml @@ -11,7 +11,6 @@ readme = "README.md" [dependencies] storage-proofs = { version = "^5.0.0", path = "../storage-proofs", default-features = false } bitvec = "0.17" -chrono = "0.4" rand = "0.7" lazy_static = "1.2" memmap = "0.7" @@ -20,11 +19,9 @@ byteorder = "1" itertools = "0.9" serde = { version = "1.0", features = ["rc", "derive"] } serde_json = "1.0" -regex = "1.3.7" ff = { version = "0.2.3", package = "fff" } blake2b_simd = "0.5" bellperson = { version = "0.12", default-features = false } -clap = "2" log = "0.4.7" fil_logger = "0.1" env_proxy = "0.4" @@ -41,15 +38,13 @@ sha2 = "0.9.1" typenum = "1.11.2" bitintr = "0.3.0" gperftools = { version = "0.2", optional = true } -phase2 = { version = "0.11", package = "phase21", default-features = false } -simplelog = "0.8.0" -rand_chacha = "0.2.1" -dialoguer = "0.7.1" generic-array = "0.14.4" structopt = "0.3.12" humansize = "1.1.0" indicatif = "0.15.0" groupy = "0.3.0" +dialoguer = "0.7.1" +clap = "2.33.3" [dependencies.reqwest] version = "0.10" @@ -70,8 +65,8 @@ heap-profile = ["gperftools/heap"] simd = ["storage-proofs/simd"] asm = ["storage-proofs/asm"] gpu = ["storage-proofs/gpu", "bellperson/gpu"] -pairing = ["storage-proofs/pairing", "bellperson/pairing", "phase2/pairing"] -blst = ["storage-proofs/blst", "bellperson/blst", "phase2/blst"] +pairing = ["storage-proofs/pairing", "bellperson/pairing"] +blst = ["storage-proofs/blst", "bellperson/blst"] [[bench]] name = "preprocessing" diff --git a/phase2/Cargo.toml b/phase2/Cargo.toml new file mode 100644 index 000000000..5642e8641 --- /dev/null +++ b/phase2/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "filecoin-phase2" +version = "0.1.0" +authors = ["dignifiedquire "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bellperson = { version = "0.12.0", default-features = false } +filecoin-proofs = { version = "5.4.0", path = "../filecoin-proofs", default-features = false } +storage-proofs = { version = "5.4.0", path = "../storage-proofs", default-features = false } +phase2 = { version = "0.11", package = "phase21", default-features = false } +groupy = "0.3.1" +log = "0.4.11" +clap = "2.33.3" +byteorder = "1.3.4" +rand = "0.7.3" +rand_chacha = "0.2.2" +simplelog = "0.8.0" +dialoguer = "0.7.1" +hex = "0.4.2" +blake2b_simd = "0.5.11" + +[features] +default = ["pairing", "gpu"] +gpu = ["bellperson/gpu", "filecoin-proofs/gpu", "storage-proofs/gpu", "phase2/gpu"] +pairing = ["bellperson/pairing", "filecoin-proofs/pairing", "storage-proofs/pairing", "phase2/pairing"] +blst = ["bellperson/blst", "filecoin-proofs/blst", "storage-proofs/blst", "phase2/blst"] diff --git a/filecoin-proofs/src/bin/phase2.rs b/phase2/src/main.rs similarity index 100% rename from filecoin-proofs/src/bin/phase2.rs rename to phase2/src/main.rs From bcfc5f35aa4acad4fa2ebf4c3a04a9c242db7d9b Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 3 Apr 2018 19:03:34 -0600 Subject: [PATCH 02/40] Initial commit --- lib.rs | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 lib.rs diff --git a/lib.rs b/lib.rs new file mode 100644 index 000000000..523417b26 --- /dev/null +++ b/lib.rs @@ -0,0 +1,425 @@ +extern crate pairing; +extern crate bellman; + +use std::{ + io::{ + self, + Read, + BufReader + }, + fs::{ + File + }, + sync::{ + Arc + } +}; + +use pairing::{ + Engine, + Field, + EncodedPoint, + CurveAffine, + CurveProjective, + bls12_381::{ + Bls12, + Fr, + G1, + G2, + G1Affine, + G1Uncompressed, + G2Affine, + G2Uncompressed + } +}; + +use bellman::{ + Circuit, + SynthesisError, + Variable, + Index, + ConstraintSystem, + LinearCombination, + groth16::{ + Parameters, + VerifyingKey + }, + multicore::Worker +}; + +/// This is our assembly structure that we'll use to synthesize the +/// circuit into a QAP. +struct KeypairAssembly { + num_inputs: usize, + num_aux: usize, + num_constraints: usize, + at_inputs: Vec>, + bt_inputs: Vec>, + ct_inputs: Vec>, + at_aux: Vec>, + bt_aux: Vec>, + ct_aux: Vec> +} + +impl ConstraintSystem for KeypairAssembly { + type Root = Self; + + fn alloc( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_aux; + self.num_aux += 1; + + self.at_aux.push(vec![]); + self.bt_aux.push(vec![]); + self.ct_aux.push(vec![]); + + Ok(Variable::new_unchecked(Index::Aux(index))) + } + + fn alloc_input( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_inputs; + self.num_inputs += 1; + + self.at_inputs.push(vec![]); + self.bt_inputs.push(vec![]); + self.ct_inputs.push(vec![]); + + Ok(Variable::new_unchecked(Index::Input(index))) + } + + fn enforce( + &mut self, + _: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + fn eval( + l: LinearCombination, + inputs: &mut [Vec<(E::Fr, usize)>], + aux: &mut [Vec<(E::Fr, usize)>], + this_constraint: usize + ) + { + for &(var, coeff) in l.as_ref() { + match var.get_unchecked() { + Index::Input(id) => inputs[id].push((coeff, this_constraint)), + Index::Aux(id) => aux[id].push((coeff, this_constraint)) + } + } + } + + eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); + eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); + eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + + self.num_constraints += 1; + } + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self) + { + // Do nothing; we don't care about namespaces in this context. + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +pub fn new_parameters( + circuit: C, +) -> Result, SynthesisError> + where C: Circuit +{ + let mut assembly = KeypairAssembly { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + at_inputs: vec![], + bt_inputs: vec![], + ct_inputs: vec![], + at_aux: vec![], + bt_aux: vec![], + ct_aux: vec![] + }; + + // Allocate the "one" input variable + assembly.alloc_input(|| "", || Ok(Fr::one()))?; + + // Synthesize the circuit. + circuit.synthesize(&mut assembly)?; + + // Input constraints to ensure full density of IC query + // x * 0 = 0 + for i in 0..assembly.num_inputs { + assembly.enforce(|| "", + |lc| lc + Variable::new_unchecked(Index::Input(i)), + |lc| lc, + |lc| lc, + ); + } + + // Compute the size of our evaluation domain + let mut m = 1; + let mut exp = 0; + while m < assembly.num_constraints { + m *= 2; + exp += 1; + + // Powers of Tau ceremony can't support more than 2^21 + if exp > 21 { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } + } + + // Try to load "phase1radix2m{}" + let f = match File::open(format!("phase1radix2m{}", exp)) { + Ok(f) => f, + Err(e) => { + panic!("Couldn't load phase1radix2m{}: {:?}", exp, e); + } + }; + let f = &mut BufReader::with_capacity(1024 * 1024, f); + + let read_g1 = |reader: &mut BufReader| -> io::Result { + let mut repr = G1Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + 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 BufReader| -> io::Result { + let mut repr = G2Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + 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 alpha = read_g1(f)?; + let beta_g1 = read_g1(f)?; + let beta_g2 = read_g2(f)?; + + let mut coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + coeffs_g1.push(read_g1(f)?); + } + + let mut coeffs_g2 = Vec::with_capacity(m); + for _ in 0..m { + coeffs_g2.push(read_g2(f)?); + } + + let mut alpha_coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + alpha_coeffs_g1.push(read_g1(f)?); + } + + let mut beta_coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + beta_coeffs_g1.push(read_g1(f)?); + } + + // These are `Arc` so that later it'll be easier + // to use multiexp during QAP evaluation (which + // requires a futures-based API) + let coeffs_g1 = Arc::new(coeffs_g1); + let coeffs_g2 = Arc::new(coeffs_g2); + let alpha_coeffs_g1 = Arc::new(alpha_coeffs_g1); + let beta_coeffs_g1 = Arc::new(beta_coeffs_g1); + + let mut h = Vec::with_capacity(m - 1); + for _ in 0..(m - 1) { + h.push(read_g1(f)?); + } + + let mut ic = vec![G1::zero(); assembly.num_inputs]; + let mut l = vec![G1::zero(); assembly.num_aux]; + let mut a_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; + + fn eval( + // Lagrange coefficients for tau + coeffs_g1: Arc>, + coeffs_g2: Arc>, + alpha_coeffs_g1: Arc>, + beta_coeffs_g1: Arc>, + + // QAP polynomials + at: &[Vec<(Fr, usize)>], + bt: &[Vec<(Fr, usize)>], + ct: &[Vec<(Fr, usize)>], + + // Resulting evaluated QAP polynomials + a_g1: &mut [G1], + b_g1: &mut [G1], + b_g2: &mut [G2], + ext: &mut [G1], + + // Worker + worker: &Worker + ) + { + // Sanity check + assert_eq!(a_g1.len(), at.len()); + assert_eq!(a_g1.len(), bt.len()); + assert_eq!(a_g1.len(), ct.len()); + assert_eq!(a_g1.len(), b_g1.len()); + assert_eq!(a_g1.len(), b_g2.len()); + assert_eq!(a_g1.len(), ext.len()); + + // Evaluate polynomials in multiple threads + worker.scope(a_g1.len(), |scope, chunk| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in + a_g1.chunks_mut(chunk) + .zip(b_g1.chunks_mut(chunk)) + .zip(b_g2.chunks_mut(chunk)) + .zip(ext.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(bt.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let coeffs_g1 = coeffs_g1.clone(); + let coeffs_g2 = coeffs_g2.clone(); + let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); + let beta_coeffs_g1 = beta_coeffs_g1.clone(); + + scope.spawn(move || { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in + a_g1.iter_mut() + .zip(b_g1.iter_mut()) + .zip(b_g2.iter_mut()) + .zip(ext.iter_mut()) + .zip(at.iter()) + .zip(bt.iter()) + .zip(ct.iter()) + { + for &(coeff, lag) in at { + a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in bt { + b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); + ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in ct { + ext.add_assign(&coeffs_g1[lag].mul(coeff)); + } + } + + // Batch normalize + G1::batch_normalization(a_g1); + G1::batch_normalization(b_g1); + G2::batch_normalization(b_g2); + G1::batch_normalization(ext); + }); + } + }); + } + + let worker = Worker::new(); + + // Evaluate for inputs. + eval( + coeffs_g1.clone(), + coeffs_g2.clone(), + alpha_coeffs_g1.clone(), + beta_coeffs_g1.clone(), + &assembly.at_inputs, + &assembly.bt_inputs, + &assembly.ct_inputs, + &mut a_g1[0..assembly.num_inputs], + &mut b_g1[0..assembly.num_inputs], + &mut b_g2[0..assembly.num_inputs], + &mut ic, + &worker + ); + + // Evaluate for auxillary variables. + eval( + coeffs_g1.clone(), + coeffs_g2.clone(), + alpha_coeffs_g1.clone(), + beta_coeffs_g1.clone(), + &assembly.at_aux, + &assembly.bt_aux, + &assembly.ct_aux, + &mut a_g1[assembly.num_inputs..], + &mut b_g1[assembly.num_inputs..], + &mut b_g2[assembly.num_inputs..], + &mut l, + &worker + ); + + // Don't allow any elements be unconstrained, so that + // the L query is always fully dense. + for e in l.iter() { + if e.is_zero() { + return Err(SynthesisError::UnconstrainedVariable); + } + } + + let vk = VerifyingKey { + alpha_g1: alpha, + beta_g1: beta_g1, + beta_g2: beta_g2, + gamma_g2: G2Affine::one(), + delta_g1: G1Affine::one(), + delta_g2: G2Affine::one(), + ic: ic.into_iter().map(|e| e.into_affine()).collect() + }; + + Ok(Parameters { + vk: vk, + h: Arc::new(h), + l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + + // Filter points at infinity away from A/B queries + a: Arc::new(a_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + }) +} From a39d6e0dd7ad244bf2f28b82cec78e038137aa61 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 5 Apr 2018 12:59:00 -0600 Subject: [PATCH 03/40] MPCParameters demonstration. --- lib.rs | 451 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 449 insertions(+), 2 deletions(-) diff --git a/lib.rs b/lib.rs index 523417b26..5182a60d8 100644 --- a/lib.rs +++ b/lib.rs @@ -1,10 +1,27 @@ extern crate pairing; extern crate bellman; +extern crate rand; +extern crate byteorder; +extern crate blake2; +extern crate num_cpus; +extern crate crossbeam; +extern crate generic_array; +extern crate typenum; + +use blake2::{Blake2b, Digest}; +use generic_array::GenericArray; +use typenum::consts::U64; + +use byteorder::{ + BigEndian, + ReadBytesExt +}; use std::{ io::{ self, Read, + Write, BufReader }, fs::{ @@ -17,10 +34,12 @@ use std::{ use pairing::{ Engine, + PrimeField, Field, EncodedPoint, CurveAffine, CurveProjective, + Wnaf, bls12_381::{ Bls12, Fr, @@ -47,6 +66,13 @@ use bellman::{ multicore::Worker }; +use rand::{ + Rng, + Rand, + ChaChaRng, + SeedableRng +}; + /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. struct KeypairAssembly { @@ -156,7 +182,7 @@ impl ConstraintSystem for KeypairAssembly { pub fn new_parameters( circuit: C, -) -> Result, SynthesisError> +) -> Result where C: Circuit { let mut assembly = KeypairAssembly { @@ -412,7 +438,7 @@ pub fn new_parameters( ic: ic.into_iter().map(|e| e.into_affine()).collect() }; - Ok(Parameters { + let params = Parameters { vk: vk, h: Arc::new(h), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), @@ -421,5 +447,426 @@ pub fn new_parameters( a: Arc::new(a_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + }; + + let h = { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + + params.write(&mut sink).unwrap(); + + sink.into_hash() + }; + + let mut cs_hash = [0; 64]; + cs_hash.copy_from_slice(h.as_ref()); + + Ok(MPCParameters { + params: params, + cs_hash: cs_hash, + contributions: vec![] }) } + +/// MPC parameters are just like bellman `Parameters` except, when serialized, +/// they contain a transcript of contributions at the end, which can be verified. +#[derive(Clone)] +pub struct MPCParameters { + params: Parameters, + cs_hash: [u8; 64], + contributions: Vec +} + +impl MPCParameters { + pub fn params(&self) -> &Parameters { + &self.params + } + + pub fn transform( + &mut self, + pubkey: &PublicKey, + privkey: &PrivateKey + ) + { + fn batch_exp(bases: &mut [C], coeff: C::Scalar) { + let coeff = coeff.into_repr(); + + let mut projective = vec![C::Projective::zero(); bases.len()]; + let cpus = num_cpus::get(); + let chunk_size = if bases.len() < cpus { + 1 + } else { + bases.len() / cpus + }; + + // Perform wNAF over multiple cores, placing results into `projective`. + crossbeam::scope(|scope| { + for (bases, projective) in bases.chunks_mut(chunk_size) + .zip(projective.chunks_mut(chunk_size)) + { + scope.spawn(move || { + let mut wnaf = Wnaf::new(); + + for (base, projective) in bases.iter_mut() + .zip(projective.iter_mut()) + { + *projective = wnaf.base(base.into_projective(), 1).scalar(coeff); + } + }); + } + }); + + // Perform batch normalization + crossbeam::scope(|scope| { + for projective in projective.chunks_mut(chunk_size) + { + scope.spawn(move || { + C::Projective::batch_normalization(projective); + }); + } + }); + + // Turn it all back into affine points + for (projective, affine) in projective.iter().zip(bases.iter_mut()) { + *affine = projective.into_affine(); + } + } + + let delta_inv = privkey.delta.inverse().unwrap(); + let mut l = (&self.params.l[..]).to_vec(); + let mut h = (&self.params.h[..]).to_vec(); + batch_exp(&mut l, delta_inv); + batch_exp(&mut h, delta_inv); + self.params.l = Arc::new(l); + self.params.h = Arc::new(h); + + self.params.vk.delta_g1 = self.params.vk.delta_g1.mul(privkey.delta).into_affine(); + self.params.vk.delta_g2 = self.params.vk.delta_g2.mul(privkey.delta).into_affine(); + + self.contributions.push(pubkey.clone()); + } +} + +#[derive(Clone)] +pub struct PublicKey { + /// This is the delta (in G1) after the transformation, kept so that we + /// can check correctness of the public keys without having the entire + /// interstitial parameters for each contribution. + delta_after: G1Affine, + + /// Random element chosen by the contributor. + s: G1Affine, + + /// That element, taken to the contributor's secret delta. + s_delta: G1Affine, + + /// r is H(last_pubkey | s | s_delta), r_delta proves knowledge of delta + r_delta: G2Affine, + + /// Hash of the transcript (used for mapping to r) + transcript: [u8; 64], +} + +impl PartialEq for PublicKey { + fn eq(&self, other: &PublicKey) -> bool { + self.delta_after == other.delta_after && + self.s == other.s && + self.s_delta == other.s_delta && + self.r_delta == other.r_delta && + &self.transcript[..] == &other.transcript[..] + } +} + +pub fn verify_transform( + before: &MPCParameters, + after: &MPCParameters +) -> bool +{ + // Parameter size doesn't change! + if before.params.vk.ic.len() != after.params.vk.ic.len() { + return false; + } + + if before.params.h.len() != after.params.h.len() { + return false; + } + + if before.params.l.len() != after.params.l.len() { + return false; + } + + if before.params.a.len() != after.params.a.len() { + return false; + } + + if before.params.b_g1.len() != after.params.b_g1.len() { + return false; + } + + if before.params.b_g2.len() != after.params.b_g2.len() { + return false; + } + + // IC shouldn't change at all, since gamma = 1 + if before.params.vk.ic != after.params.vk.ic { + return false; + } + + // Transformations involve a single new contribution + if after.contributions.len() != (before.contributions.len() + 1) { + return false; + } + + // All of the previous pubkeys should be the same + if &before.contributions[..] != &after.contributions[0..before.contributions.len()] { + return false; + } + + let pubkey = after.contributions.last().unwrap(); + + // The new pubkey's claimed value of delta should match the + // parameters + if pubkey.delta_after != after.params.vk.delta_g1 { + return false; + } + + // The `cs_hash` should not change. It's initialized at the beginning + if &before.cs_hash[..] != &after.cs_hash[..] { + return false; + } + + // H(cs_hash | | s | s_delta) + let h = { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + + sink.write_all(&before.cs_hash[..]).unwrap(); + for pubkey in &before.contributions { + sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + } + sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); + sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + + sink.into_hash() + }; + + // The transcript must be consistent + if &pubkey.transcript[..] != h.as_ref() { + return false; + } + + let r = hash_to_g2(h.as_ref()).into_affine(); + + // Check the signature of knowledge + if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { + return false; + } + + // Check the change from the old delta is consistent + if !same_ratio( + (before.params.vk.delta_g1, after.params.vk.delta_g1), + (r, pubkey.r_delta) + ) { + return false; + } + + if !same_ratio( + (before.params.vk.delta_g2, after.params.vk.delta_g2), + (pubkey.s, pubkey.s_delta) + ) { + return false; + } + + // H and L queries should be updated + if !same_ratio( + merge_pairs(&before.params.h, &after.params.h), + (pubkey.r_delta, r) // reversed for inverse + ) { + return false; + } + + if !same_ratio( + merge_pairs(&before.params.l, &after.params.l), + (pubkey.r_delta, r) // reversed for inverse + ) { + return false; + } + + true +} + +/// Checks if pairs have the same ratio. +fn same_ratio( + g1: (G1, G1), + g2: (G1::Pair, G1::Pair) +) -> bool +{ + g1.0.pairing_with(&g2.1) == g1.1.pairing_with(&g2.0) +} + +/// Computes a random linear combination over v1/v2. +/// +/// Checking that many pairs of elements are exponentiated by +/// the same `x` can be achieved (with high probability) with +/// the following technique: +/// +/// Given v1 = [a, b, c] and v2 = [as, bs, cs], compute +/// (a*r1 + b*r2 + c*r3, (as)*r1 + (bs)*r2 + (cs)*r3) for some +/// random r1, r2, r3. Given (g, g^s)... +/// +/// e(g, (as)*r1 + (bs)*r2 + (cs)*r3) = e(g^s, a*r1 + b*r2 + c*r3) +/// +/// ... with high probability. +fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) +{ + use std::sync::{Arc, Mutex}; + use rand::{thread_rng}; + + assert_eq!(v1.len(), v2.len()); + + let chunk = (v1.len() / num_cpus::get()) + 1; + + let s = Arc::new(Mutex::new(G::Projective::zero())); + let sx = Arc::new(Mutex::new(G::Projective::zero())); + + crossbeam::scope(|scope| { + for (v1, v2) in v1.chunks(chunk).zip(v2.chunks(chunk)) { + let s = s.clone(); + let sx = sx.clone(); + + scope.spawn(move || { + // We do not need to be overly cautious of the RNG + // used for this check. + let rng = &mut thread_rng(); + + let mut wnaf = Wnaf::new(); + let mut local_s = G::Projective::zero(); + let mut local_sx = G::Projective::zero(); + + for (v1, v2) in v1.iter().zip(v2.iter()) { + let rho = G::Scalar::rand(rng); + let mut wnaf = wnaf.scalar(rho.into_repr()); + let v1 = wnaf.base(v1.into_projective()); + let v2 = wnaf.base(v2.into_projective()); + + local_s.add_assign(&v1); + local_sx.add_assign(&v2); + } + + s.lock().unwrap().add_assign(&local_s); + sx.lock().unwrap().add_assign(&local_sx); + }); + } + }); + + let s = s.lock().unwrap().into_affine(); + let sx = sx.lock().unwrap().into_affine(); + + (s, sx) +} + +pub struct PrivateKey { + delta: Fr +} + +pub fn keypair( + rng: &mut R, + current: &MPCParameters, +) -> (PublicKey, PrivateKey) +{ + // Sample random delta + let delta: Fr = rng.gen(); + + // Compute delta s-pair in G1 + let s = G1::rand(rng).into_affine(); + let s_delta = s.mul(delta).into_affine(); + + // H(cs_hash | | s | s_delta) + let h = { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + + sink.write_all(¤t.cs_hash[..]).unwrap(); + for pubkey in ¤t.contributions { + sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + } + sink.write_all(s.into_uncompressed().as_ref()).unwrap(); + sink.write_all(s_delta.into_uncompressed().as_ref()).unwrap(); + + sink.into_hash() + }; + + // This avoids making a weird assumption about the hash into the + // group. + let mut transcript = [0; 64]; + transcript.copy_from_slice(h.as_ref()); + + // Compute delta s-pair in G2 + let r = hash_to_g2(h.as_ref()).into_affine(); + let r_delta = r.mul(delta).into_affine(); + + ( + PublicKey { + delta_after: current.params.vk.delta_g1.mul(delta).into_affine(), + s: s, + s_delta: s_delta, + r_delta: r_delta, + transcript: transcript + }, + PrivateKey { + delta: delta + } + ) +} + +/// Hashes to G2 using the first 32 bytes of `digest`. Panics if `digest` is less +/// than 32 bytes. +fn hash_to_g2(mut digest: &[u8]) -> G2 +{ + assert!(digest.len() >= 32); + + let mut seed = Vec::with_capacity(8); + + for _ in 0..8 { + seed.push(digest.read_u32::().expect("assertion above guarantees this to work")); + } + + ChaChaRng::from_seed(&seed).gen() +} + +/// Abstraction over a writer which hashes the data being written. +pub struct HashWriter { + writer: W, + hasher: Blake2b +} + +impl HashWriter { + /// Construct a new `HashWriter` given an existing `writer` by value. + pub fn new(writer: W) -> Self { + HashWriter { + writer: writer, + hasher: Blake2b::default() + } + } + + /// Destroy this writer and return the hash of what was written. + pub fn into_hash(self) -> GenericArray { + self.hasher.result() + } +} + +impl Write for HashWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let bytes = self.writer.write(buf)?; + + if bytes > 0 { + self.hasher.input(&buf[0..bytes]); + } + + Ok(bytes) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} From 48f98a91202dfac8faadef4fae7050a6a8ff45e0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 5 Apr 2018 13:48:37 -0600 Subject: [PATCH 04/40] Serialization of MPCParameters and PublicKey. --- lib.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 5182a60d8..23a942208 100644 --- a/lib.rs +++ b/lib.rs @@ -14,7 +14,8 @@ use typenum::consts::U64; use byteorder::{ BigEndian, - ReadBytesExt + ReadBytesExt, + WriteBytesExt }; use std::{ @@ -477,7 +478,53 @@ pub struct MPCParameters { contributions: Vec } +impl PartialEq for MPCParameters { + fn eq(&self, other: &MPCParameters) -> bool { + self.params == other.params && + &self.cs_hash[..] == &other.cs_hash[..] && + self.contributions == other.contributions + } +} + impl MPCParameters { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + self.params.write(&mut writer)?; + writer.write_all(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + + Ok(()) + } + + pub fn read( + mut reader: R, + checked: bool + ) -> io::Result + { + let params = Parameters::read(&mut reader, checked)?; + + let mut cs_hash = [0u8; 64]; + reader.read_exact(&mut cs_hash)?; + + let contributions_len = reader.read_u32::()? as usize; + + let mut contributions = vec![]; + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut reader)?); + } + + Ok(MPCParameters { + params, cs_hash, contributions + }) + } + pub fn params(&self) -> &Parameters { &self.params } @@ -567,6 +614,65 @@ pub struct PublicKey { transcript: [u8; 64], } +impl PublicKey { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.delta_after.into_uncompressed().as_ref())?; + writer.write_all(self.s.into_uncompressed().as_ref())?; + writer.write_all(self.s_delta.into_uncompressed().as_ref())?; + writer.write_all(self.r_delta.into_uncompressed().as_ref())?; + writer.write_all(&self.transcript)?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = G1Uncompressed::empty(); + let mut g2_repr = G2Uncompressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let delta_after = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if delta_after.is_zero() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + } + + reader.read_exact(g1_repr.as_mut())?; + let s = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if s.is_zero() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + } + + reader.read_exact(g1_repr.as_mut())?; + let s_delta = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if s_delta.is_zero() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + } + + reader.read_exact(g2_repr.as_mut())?; + let r_delta = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if r_delta.is_zero() { + return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + } + + let mut transcript = [0u8; 64]; + reader.read_exact(&mut transcript)?; + + Ok(PublicKey { + delta_after, s, s_delta, r_delta, transcript + }) + } +} + impl PartialEq for PublicKey { fn eq(&self, other: &PublicKey) -> bool { self.delta_after == other.delta_after && From b97790a7865476a4469946c6f6eeccd200744de0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 5 Apr 2018 15:13:29 -0600 Subject: [PATCH 05/40] Verify transformations using a full verifier --- lib.rs | 266 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 158 insertions(+), 108 deletions(-) diff --git a/lib.rs b/lib.rs index 23a942208..b29c330b5 100644 --- a/lib.rs +++ b/lib.rs @@ -533,7 +533,7 @@ impl MPCParameters { &mut self, pubkey: &PublicKey, privkey: &PrivateKey - ) + ) -> [u8; 64] { fn batch_exp(bases: &mut [C], coeff: C::Scalar) { let coeff = coeff.into_repr(); @@ -591,6 +591,146 @@ impl MPCParameters { self.params.vk.delta_g2 = self.params.vk.delta_g2.mul(privkey.delta).into_affine(); self.contributions.push(pubkey.clone()); + + { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + pubkey.write(&mut sink).unwrap(); + let h = sink.into_hash(); + let mut response = [0u8; 64]; + response.copy_from_slice(h.as_ref()); + response + } + } + + pub fn verify>( + &self, + circuit: C + ) -> Result, ()> + { + let initial_params = new_parameters(circuit).map_err(|_| ())?; + + // H/L will change, but should have same length + if initial_params.params.h.len() != self.params.h.len() { + return Err(()); + } + if initial_params.params.l.len() != self.params.l.len() { + return Err(()); + } + + // A/B_G1/B_G2 doesn't change at all + if initial_params.params.a != self.params.a { + return Err(()); + } + if initial_params.params.b_g1 != self.params.b_g1 { + return Err(()); + } + if initial_params.params.b_g2 != self.params.b_g2 { + return Err(()); + } + + // alpha/beta/gamma don't change + if initial_params.params.vk.alpha_g1 != self.params.vk.alpha_g1 { + return Err(()); + } + if initial_params.params.vk.beta_g1 != self.params.vk.beta_g1 { + return Err(()); + } + if initial_params.params.vk.beta_g2 != self.params.vk.beta_g2 { + return Err(()); + } + if initial_params.params.vk.gamma_g2 != self.params.vk.gamma_g2 { + return Err(()); + } + + // IC shouldn't change, as gamma doesn't change + if initial_params.params.vk.ic != self.params.vk.ic { + return Err(()); + } + + // cs_hash should be the same + if &initial_params.cs_hash[..] != &self.cs_hash[..] { + return Err(()); + } + + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + sink.write_all(&initial_params.cs_hash[..]).unwrap(); + + let mut current_delta = G1Affine::one(); + let mut result = vec![]; + + for pubkey in &self.contributions { + let mut our_sink = sink.clone(); + our_sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); + our_sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + + sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + + let h = our_sink.into_hash(); + + // The transcript must be consistent + if &pubkey.transcript[..] != h.as_ref() { + return Err(()); + } + + let r = hash_to_g2(h.as_ref()).into_affine(); + + // Check the signature of knowledge + if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { + return Err(()); + } + + // Check the change from the old delta is consistent + if !same_ratio( + (current_delta, pubkey.delta_after), + (r, pubkey.r_delta) + ) { + return Err(()); + } + + current_delta = pubkey.delta_after; + + { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + pubkey.write(&mut sink).unwrap(); + let h = sink.into_hash(); + let mut response = [0u8; 64]; + response.copy_from_slice(h.as_ref()); + result.push(response); + } + } + + // Current parameters should have consistent delta in G1 + if current_delta != self.params.vk.delta_g1 { + return Err(()); + } + + // Current parameters should have consistent delta in G2 + if !same_ratio( + (G1Affine::one(), current_delta), + (G2Affine::one(), self.params.vk.delta_g2) + ) { + return Err(()); + } + + // H and L queries should be updated with delta^-1 + if !same_ratio( + merge_pairs(&initial_params.params.h, &self.params.h), + (self.params.vk.delta_g2, G2Affine::one()) // reversed for inverse + ) { + return Err(()); + } + + if !same_ratio( + merge_pairs(&initial_params.params.l, &self.params.l), + (self.params.vk.delta_g2, G2Affine::one()) // reversed for inverse + ) { + return Err(()); + } + + Ok(result) } } @@ -683,122 +823,23 @@ impl PartialEq for PublicKey { } } -pub fn verify_transform( +pub fn verify_transform>( + circuit: C, before: &MPCParameters, after: &MPCParameters -) -> bool +) -> Result, ()> { - // Parameter size doesn't change! - if before.params.vk.ic.len() != after.params.vk.ic.len() { - return false; - } - - if before.params.h.len() != after.params.h.len() { - return false; - } - - if before.params.l.len() != after.params.l.len() { - return false; - } - - if before.params.a.len() != after.params.a.len() { - return false; - } - - if before.params.b_g1.len() != after.params.b_g1.len() { - return false; - } - - if before.params.b_g2.len() != after.params.b_g2.len() { - return false; - } - - // IC shouldn't change at all, since gamma = 1 - if before.params.vk.ic != after.params.vk.ic { - return false; - } - - // Transformations involve a single new contribution + // Transformation involves a single new object if after.contributions.len() != (before.contributions.len() + 1) { - return false; + return Err(()); } - // All of the previous pubkeys should be the same + // None of the previous transformations should change if &before.contributions[..] != &after.contributions[0..before.contributions.len()] { - return false; - } - - let pubkey = after.contributions.last().unwrap(); - - // The new pubkey's claimed value of delta should match the - // parameters - if pubkey.delta_after != after.params.vk.delta_g1 { - return false; - } - - // The `cs_hash` should not change. It's initialized at the beginning - if &before.cs_hash[..] != &after.cs_hash[..] { - return false; - } - - // H(cs_hash | | s | s_delta) - let h = { - let sink = io::sink(); - let mut sink = HashWriter::new(sink); - - sink.write_all(&before.cs_hash[..]).unwrap(); - for pubkey in &before.contributions { - sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); - } - sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); - sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); - - sink.into_hash() - }; - - // The transcript must be consistent - if &pubkey.transcript[..] != h.as_ref() { - return false; - } - - let r = hash_to_g2(h.as_ref()).into_affine(); - - // Check the signature of knowledge - if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { - return false; + return Err(()); } - // Check the change from the old delta is consistent - if !same_ratio( - (before.params.vk.delta_g1, after.params.vk.delta_g1), - (r, pubkey.r_delta) - ) { - return false; - } - - if !same_ratio( - (before.params.vk.delta_g2, after.params.vk.delta_g2), - (pubkey.s, pubkey.s_delta) - ) { - return false; - } - - // H and L queries should be updated - if !same_ratio( - merge_pairs(&before.params.h, &after.params.h), - (pubkey.r_delta, r) // reversed for inverse - ) { - return false; - } - - if !same_ratio( - merge_pairs(&before.params.l, &after.params.l), - (pubkey.r_delta, r) // reversed for inverse - ) { - return false; - } - - true + after.verify(circuit) } /// Checks if pairs have the same ratio. @@ -946,6 +987,15 @@ pub struct HashWriter { hasher: Blake2b } +impl Clone for HashWriter { + fn clone(&self) -> HashWriter { + HashWriter { + writer: io::sink(), + hasher: self.hasher.clone() + } + } +} + impl HashWriter { /// Construct a new `HashWriter` given an existing `writer` by value. pub fn new(writer: W) -> Self { From a5e021f50e4416c57290f3c47daa6d7fe4a4c651 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 5 Apr 2018 18:41:15 -0600 Subject: [PATCH 06/40] Clean up API and add comments --- lib.rs | 895 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 575 insertions(+), 320 deletions(-) diff --git a/lib.rs b/lib.rs index b29c330b5..25f103a24 100644 --- a/lib.rs +++ b/lib.rs @@ -1,3 +1,206 @@ +//! # zk-SNARK MPCs, made easy. +//! +//! ## Make your circuit +//! +//! Grab the [`bellman`](https://github.com/ebfull/bellman) and +//! [`pairing`](https://github.com/ebfull/bellman) crates. Bellman +//! provides a trait called `Circuit`, which you must implement +//! for your computation. +//! +//! Here's a silly example: proving you know the cube root of +//! a field element. +//! +//! ```rust +//! extern crate pairing; +//! extern crate bellman; +//! +//! use pairing::{Engine, Field}; +//! use bellman::{ +//! Circuit, +//! ConstraintSystem, +//! SynthesisError, +//! }; +//! +//! struct CubeRoot { +//! cube_root: Option +//! } +//! +//! impl Circuit for CubeRoot { +//! fn synthesize>( +//! self, +//! cs: &mut CS +//! ) -> Result<(), SynthesisError> +//! { +//! // Witness the cube root +//! let root = cs.alloc(|| "root", || { +//! self.cube_root.ok_or(SynthesisError::AssignmentMissing) +//! })?; +//! +//! // Witness the square of the cube root +//! let square = cs.alloc(|| "square", || { +//! self.cube_root +//! .ok_or(SynthesisError::AssignmentMissing) +//! .map(|mut root| {root.square(); root }) +//! })?; +//! +//! // Enforce that `square` is root^2 +//! cs.enforce( +//! || "squaring", +//! |lc| lc + root, +//! |lc| lc + root, +//! |lc| lc + square +//! ); +//! +//! // Witness the cube, as a public input +//! let cube = cs.alloc_input(|| "cube", || { +//! self.cube_root +//! .ok_or(SynthesisError::AssignmentMissing) +//! .map(|root| { +//! let mut tmp = root; +//! tmp.square(); +//! tmp.mul_assign(&root); +//! tmp +//! }) +//! })?; +//! +//! // Enforce that `cube` is root^3 +//! // i.e. that `cube` is `root` * `square` +//! cs.enforce( +//! || "cubing", +//! |lc| lc + root, +//! |lc| lc + square, +//! |lc| lc + cube +//! ); +//! +//! Ok(()) +//! } +//! } +//! ``` +//! +//! ## Create some proofs +//! +//! Now that we have `CubeRoot` implementing `Circuit`, +//! let's create some parameters and make some proofs. +//! +//! ```rust,ignore +//! extern crate rand; +//! +//! use pairing::bls12_381::{Bls12, Fr}; +//! use bellman::groth16::{ +//! generate_random_parameters, +//! create_random_proof, +//! prepare_verifying_key, +//! verify_proof +//! }; +//! use rand::{OsRng, Rand}; +//! +//! let rng = &mut OsRng::new(); +//! +//! // Create public parameters for our circuit +//! let params = { +//! let circuit = CubeRoot:: { +//! cube_root: None +//! }; +//! +//! generate_random_parameters::( +//! circuit, +//! rng +//! ).unwrap() +//! }; +//! +//! // Prepare the verifying key for verification +//! let pvk = prepare_verifying_key(¶ms.vk); +//! +//! // Let's start making proofs! +//! for _ in 0..50 { +//! // Verifier picks a cube in the field. +//! // Let's just make a random one. +//! let root = Fr::rand(rng); +//! let mut cube = root; +//! cube.square(); +//! cube.mul_assign(&root); +//! +//! // Prover gets the cube, figures out the cube +//! // root, and makes the proof: +//! let proof = create_random_proof( +//! CubeRoot:: { +//! cube_root: Some(root) +//! }, ¶ms, rng +//! ).unwrap(); +//! +//! // Verifier checks the proof against the cube +//! assert!(verify_proof(&pvk, &proof, &[cube]).unwrap()); +//! } +//! ``` +//! ## Creating parameters +//! +//! Notice in the previous example that we created our zk-SNARK +//! parameters by calling `generate_random_parameters`. However, +//! if you wanted you could have called `generate_parameters` +//! with some secret numbers you chose, and kept them for +//! yourself. Given those numbers, you can create false proofs. +//! +//! In order to convince others you didn't, a multi-party +//! computation (MPC) can be used. The MPC has the property that +//! only one participant needs to be honest for the parameters to +//! be secure. This crate (`phase2`) is about creating parameters +//! securely using such an MPC. +//! +//! Let's start by using `phase2` to create some base parameters +//! for our circuit: +//! +//! ```rust,ignore +//! extern crate phase2; +//! +//! let mut params = phase2::MPCParameters::new(CubeRoot { +//! cube_root: None +//! }).unwrap(); +//! ``` +//! +//! The first time you try this, it will try to read a file like +//! `phase1radix2m2` from the current directory. You need to grab +//! that from the Powers of Tau. +//! +//! These parameters are not safe to use; false proofs can be +//! created for them. Let's contribute some randomness to these +//! parameters. +//! +//! ```rust,ignore +//! // Create a random keypair for our parameters +//! let (pubkey, privkey) = phase2::keypair(rng, ¶ms); +//! +//! // Contribute to the parameters. Remember this hash, it's +//! // how we know our contribution is in the parameters! +//! let hash = params.contribute(&pubkey, &privkey); +//! +//! // Throw away the private key! +//! drop(privkey); +//! ``` +//! +//! These parameters are now secure to use, so long as you destroyed +//! the privkey. That may not be convincing to others, so let them +//! contribute randomness too! `params` can be serialized and sent +//! elsewhere, where they can do the same thing and send new +//! parameters back to you. Only one person needs to destroy the +//! `privkey` for the final parameters to be secure. +//! +//! Once you're done setting up the parameters, you can verify the +//! parameters: +//! +//! ```rust,ignore +//! let contributions = params.verify(CubeRoot { +//! cube_root: None +//! }).expect("parameters should be valid!"); +//! +//! // We need to check the `contributions` to see if our `hash` +//! // is in it (see above, when we first contributed) +//! assert!(phase2::contains_contribution(&contributions, &hash)); +//! ``` +//! +//! Great, now if you're happy, grab the Groth16 `Parameters` with +//! `params.params()`, so that you can interact with the bellman APIs +//! just as before. + extern crate pairing; extern crate bellman; extern crate rand; @@ -181,355 +384,331 @@ impl ConstraintSystem for KeypairAssembly { } } -pub fn new_parameters( - circuit: C, -) -> Result - where C: Circuit -{ - let mut assembly = KeypairAssembly { - num_inputs: 0, - num_aux: 0, - num_constraints: 0, - at_inputs: vec![], - bt_inputs: vec![], - ct_inputs: vec![], - at_aux: vec![], - bt_aux: vec![], - ct_aux: vec![] - }; - - // Allocate the "one" input variable - assembly.alloc_input(|| "", || Ok(Fr::one()))?; - - // Synthesize the circuit. - circuit.synthesize(&mut assembly)?; +/// MPC parameters are just like bellman `Parameters` except, when serialized, +/// they contain a transcript of contributions at the end, which can be verified. +#[derive(Clone)] +pub struct MPCParameters { + params: Parameters, + cs_hash: [u8; 64], + contributions: Vec +} - // Input constraints to ensure full density of IC query - // x * 0 = 0 - for i in 0..assembly.num_inputs { - assembly.enforce(|| "", - |lc| lc + Variable::new_unchecked(Index::Input(i)), - |lc| lc, - |lc| lc, - ); +impl PartialEq for MPCParameters { + fn eq(&self, other: &MPCParameters) -> bool { + self.params == other.params && + &self.cs_hash[..] == &other.cs_hash[..] && + self.contributions == other.contributions } +} - // Compute the size of our evaluation domain - let mut m = 1; - let mut exp = 0; - while m < assembly.num_constraints { - m *= 2; - exp += 1; - - // Powers of Tau ceremony can't support more than 2^21 - if exp > 21 { - return Err(SynthesisError::PolynomialDegreeTooLarge) +impl MPCParameters { + /// Create new Groth16 parameters (compatible with bellman) for a + /// given circuit. The resulting parameters are unsafe to use + /// until there are contributions (see `transform`). + pub fn new( + circuit: C, + ) -> Result + where C: Circuit + { + let mut assembly = KeypairAssembly { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + at_inputs: vec![], + bt_inputs: vec![], + ct_inputs: vec![], + at_aux: vec![], + bt_aux: vec![], + ct_aux: vec![] + }; + + // Allocate the "one" input variable + assembly.alloc_input(|| "", || Ok(Fr::one()))?; + + // Synthesize the circuit. + circuit.synthesize(&mut assembly)?; + + // Input constraints to ensure full density of IC query + // x * 0 = 0 + for i in 0..assembly.num_inputs { + assembly.enforce(|| "", + |lc| lc + Variable::new_unchecked(Index::Input(i)), + |lc| lc, + |lc| lc, + ); } - } - // Try to load "phase1radix2m{}" - let f = match File::open(format!("phase1radix2m{}", exp)) { - Ok(f) => f, - Err(e) => { - panic!("Couldn't load phase1radix2m{}: {:?}", exp, e); + // Compute the size of our evaluation domain + let mut m = 1; + let mut exp = 0; + while m < assembly.num_constraints { + m *= 2; + exp += 1; + + // Powers of Tau ceremony can't support more than 2^21 + if exp > 21 { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } } - }; - let f = &mut BufReader::with_capacity(1024 * 1024, f); - - let read_g1 = |reader: &mut BufReader| -> io::Result { - let mut repr = G1Uncompressed::empty(); - reader.read_exact(repr.as_mut())?; - - 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 BufReader| -> io::Result { - let mut repr = G2Uncompressed::empty(); - reader.read_exact(repr.as_mut())?; + // Try to load "phase1radix2m{}" + let f = match File::open(format!("phase1radix2m{}", exp)) { + Ok(f) => f, + Err(e) => { + panic!("Couldn't load phase1radix2m{}: {:?}", exp, e); + } + }; + let f = &mut BufReader::with_capacity(1024 * 1024, f); - 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_g1 = |reader: &mut BufReader| -> io::Result { + let mut repr = G1Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; - let alpha = read_g1(f)?; - let beta_g1 = read_g1(f)?; - let beta_g2 = read_g2(f)?; + 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 BufReader| -> io::Result { + let mut repr = G2Uncompressed::empty(); + reader.read_exact(repr.as_mut())?; + + 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 mut coeffs_g1 = Vec::with_capacity(m); - for _ in 0..m { - coeffs_g1.push(read_g1(f)?); - } + let alpha = read_g1(f)?; + let beta_g1 = read_g1(f)?; + let beta_g2 = read_g2(f)?; - let mut coeffs_g2 = Vec::with_capacity(m); - for _ in 0..m { - coeffs_g2.push(read_g2(f)?); - } + let mut coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + coeffs_g1.push(read_g1(f)?); + } - let mut alpha_coeffs_g1 = Vec::with_capacity(m); - for _ in 0..m { - alpha_coeffs_g1.push(read_g1(f)?); - } + let mut coeffs_g2 = Vec::with_capacity(m); + for _ in 0..m { + coeffs_g2.push(read_g2(f)?); + } - let mut beta_coeffs_g1 = Vec::with_capacity(m); - for _ in 0..m { - beta_coeffs_g1.push(read_g1(f)?); - } + let mut alpha_coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + alpha_coeffs_g1.push(read_g1(f)?); + } - // These are `Arc` so that later it'll be easier - // to use multiexp during QAP evaluation (which - // requires a futures-based API) - let coeffs_g1 = Arc::new(coeffs_g1); - let coeffs_g2 = Arc::new(coeffs_g2); - let alpha_coeffs_g1 = Arc::new(alpha_coeffs_g1); - let beta_coeffs_g1 = Arc::new(beta_coeffs_g1); - - let mut h = Vec::with_capacity(m - 1); - for _ in 0..(m - 1) { - h.push(read_g1(f)?); - } + let mut beta_coeffs_g1 = Vec::with_capacity(m); + for _ in 0..m { + beta_coeffs_g1.push(read_g1(f)?); + } - let mut ic = vec![G1::zero(); assembly.num_inputs]; - let mut l = vec![G1::zero(); assembly.num_aux]; - let mut a_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; - let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; - let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; - - fn eval( - // Lagrange coefficients for tau - coeffs_g1: Arc>, - coeffs_g2: Arc>, - alpha_coeffs_g1: Arc>, - beta_coeffs_g1: Arc>, - - // QAP polynomials - at: &[Vec<(Fr, usize)>], - bt: &[Vec<(Fr, usize)>], - ct: &[Vec<(Fr, usize)>], - - // Resulting evaluated QAP polynomials - a_g1: &mut [G1], - b_g1: &mut [G1], - b_g2: &mut [G2], - ext: &mut [G1], - - // Worker - worker: &Worker - ) - { - // Sanity check - assert_eq!(a_g1.len(), at.len()); - assert_eq!(a_g1.len(), bt.len()); - assert_eq!(a_g1.len(), ct.len()); - assert_eq!(a_g1.len(), b_g1.len()); - assert_eq!(a_g1.len(), b_g2.len()); - assert_eq!(a_g1.len(), ext.len()); - - // Evaluate polynomials in multiple threads - worker.scope(a_g1.len(), |scope, chunk| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in - a_g1.chunks_mut(chunk) - .zip(b_g1.chunks_mut(chunk)) - .zip(b_g2.chunks_mut(chunk)) - .zip(ext.chunks_mut(chunk)) - .zip(at.chunks(chunk)) - .zip(bt.chunks(chunk)) - .zip(ct.chunks(chunk)) - { - let coeffs_g1 = coeffs_g1.clone(); - let coeffs_g2 = coeffs_g2.clone(); - let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); - let beta_coeffs_g1 = beta_coeffs_g1.clone(); - - scope.spawn(move || { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in - a_g1.iter_mut() - .zip(b_g1.iter_mut()) - .zip(b_g2.iter_mut()) - .zip(ext.iter_mut()) - .zip(at.iter()) - .zip(bt.iter()) - .zip(ct.iter()) - { - for &(coeff, lag) in at { - a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); - } + // These are `Arc` so that later it'll be easier + // to use multiexp during QAP evaluation (which + // requires a futures-based API) + let coeffs_g1 = Arc::new(coeffs_g1); + let coeffs_g2 = Arc::new(coeffs_g2); + let alpha_coeffs_g1 = Arc::new(alpha_coeffs_g1); + let beta_coeffs_g1 = Arc::new(beta_coeffs_g1); + + let mut h = Vec::with_capacity(m - 1); + for _ in 0..(m - 1) { + h.push(read_g1(f)?); + } - for &(coeff, lag) in bt { - b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); - ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); - } + let mut ic = vec![G1::zero(); assembly.num_inputs]; + let mut l = vec![G1::zero(); assembly.num_aux]; + let mut a_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; + + fn eval( + // Lagrange coefficients for tau + coeffs_g1: Arc>, + coeffs_g2: Arc>, + alpha_coeffs_g1: Arc>, + beta_coeffs_g1: Arc>, + + // QAP polynomials + at: &[Vec<(Fr, usize)>], + bt: &[Vec<(Fr, usize)>], + ct: &[Vec<(Fr, usize)>], + + // Resulting evaluated QAP polynomials + a_g1: &mut [G1], + b_g1: &mut [G1], + b_g2: &mut [G2], + ext: &mut [G1], + + // Worker + worker: &Worker + ) + { + // Sanity check + assert_eq!(a_g1.len(), at.len()); + assert_eq!(a_g1.len(), bt.len()); + assert_eq!(a_g1.len(), ct.len()); + assert_eq!(a_g1.len(), b_g1.len()); + assert_eq!(a_g1.len(), b_g2.len()); + assert_eq!(a_g1.len(), ext.len()); + + // Evaluate polynomials in multiple threads + worker.scope(a_g1.len(), |scope, chunk| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in + a_g1.chunks_mut(chunk) + .zip(b_g1.chunks_mut(chunk)) + .zip(b_g2.chunks_mut(chunk)) + .zip(ext.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(bt.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let coeffs_g1 = coeffs_g1.clone(); + let coeffs_g2 = coeffs_g2.clone(); + let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); + let beta_coeffs_g1 = beta_coeffs_g1.clone(); - for &(coeff, lag) in ct { - ext.add_assign(&coeffs_g1[lag].mul(coeff)); + scope.spawn(move || { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in + a_g1.iter_mut() + .zip(b_g1.iter_mut()) + .zip(b_g2.iter_mut()) + .zip(ext.iter_mut()) + .zip(at.iter()) + .zip(bt.iter()) + .zip(ct.iter()) + { + for &(coeff, lag) in at { + a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in bt { + b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); + ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in ct { + ext.add_assign(&coeffs_g1[lag].mul(coeff)); + } } - } - - // Batch normalize - G1::batch_normalization(a_g1); - G1::batch_normalization(b_g1); - G2::batch_normalization(b_g2); - G1::batch_normalization(ext); - }); - } - }); - } - let worker = Worker::new(); - - // Evaluate for inputs. - eval( - coeffs_g1.clone(), - coeffs_g2.clone(), - alpha_coeffs_g1.clone(), - beta_coeffs_g1.clone(), - &assembly.at_inputs, - &assembly.bt_inputs, - &assembly.ct_inputs, - &mut a_g1[0..assembly.num_inputs], - &mut b_g1[0..assembly.num_inputs], - &mut b_g2[0..assembly.num_inputs], - &mut ic, - &worker - ); - - // Evaluate for auxillary variables. - eval( - coeffs_g1.clone(), - coeffs_g2.clone(), - alpha_coeffs_g1.clone(), - beta_coeffs_g1.clone(), - &assembly.at_aux, - &assembly.bt_aux, - &assembly.ct_aux, - &mut a_g1[assembly.num_inputs..], - &mut b_g1[assembly.num_inputs..], - &mut b_g2[assembly.num_inputs..], - &mut l, - &worker - ); - - // Don't allow any elements be unconstrained, so that - // the L query is always fully dense. - for e in l.iter() { - if e.is_zero() { - return Err(SynthesisError::UnconstrainedVariable); + // Batch normalize + G1::batch_normalization(a_g1); + G1::batch_normalization(b_g1); + G2::batch_normalization(b_g2); + G1::batch_normalization(ext); + }); + } + }); } - } - - let vk = VerifyingKey { - alpha_g1: alpha, - beta_g1: beta_g1, - beta_g2: beta_g2, - gamma_g2: G2Affine::one(), - delta_g1: G1Affine::one(), - delta_g2: G2Affine::one(), - ic: ic.into_iter().map(|e| e.into_affine()).collect() - }; - - let params = Parameters { - vk: vk, - h: Arc::new(h), - l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), - - // Filter points at infinity away from A/B queries - a: Arc::new(a_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), - b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), - b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) - }; - let h = { - let sink = io::sink(); - let mut sink = HashWriter::new(sink); - - params.write(&mut sink).unwrap(); - - sink.into_hash() - }; - - let mut cs_hash = [0; 64]; - cs_hash.copy_from_slice(h.as_ref()); - - Ok(MPCParameters { - params: params, - cs_hash: cs_hash, - contributions: vec![] - }) -} - -/// MPC parameters are just like bellman `Parameters` except, when serialized, -/// they contain a transcript of contributions at the end, which can be verified. -#[derive(Clone)] -pub struct MPCParameters { - params: Parameters, - cs_hash: [u8; 64], - contributions: Vec -} - -impl PartialEq for MPCParameters { - fn eq(&self, other: &MPCParameters) -> bool { - self.params == other.params && - &self.cs_hash[..] == &other.cs_hash[..] && - self.contributions == other.contributions - } -} + let worker = Worker::new(); + + // Evaluate for inputs. + eval( + coeffs_g1.clone(), + coeffs_g2.clone(), + alpha_coeffs_g1.clone(), + beta_coeffs_g1.clone(), + &assembly.at_inputs, + &assembly.bt_inputs, + &assembly.ct_inputs, + &mut a_g1[0..assembly.num_inputs], + &mut b_g1[0..assembly.num_inputs], + &mut b_g2[0..assembly.num_inputs], + &mut ic, + &worker + ); -impl MPCParameters { - pub fn write( - &self, - mut writer: W - ) -> io::Result<()> - { - self.params.write(&mut writer)?; - writer.write_all(&self.cs_hash)?; + // Evaluate for auxillary variables. + eval( + coeffs_g1.clone(), + coeffs_g2.clone(), + alpha_coeffs_g1.clone(), + beta_coeffs_g1.clone(), + &assembly.at_aux, + &assembly.bt_aux, + &assembly.ct_aux, + &mut a_g1[assembly.num_inputs..], + &mut b_g1[assembly.num_inputs..], + &mut b_g2[assembly.num_inputs..], + &mut l, + &worker + ); - writer.write_u32::(self.contributions.len() as u32)?; - for pubkey in &self.contributions { - pubkey.write(&mut writer)?; + // Don't allow any elements be unconstrained, so that + // the L query is always fully dense. + for e in l.iter() { + if e.is_zero() { + return Err(SynthesisError::UnconstrainedVariable); + } } - Ok(()) - } + let vk = VerifyingKey { + alpha_g1: alpha, + beta_g1: beta_g1, + beta_g2: beta_g2, + gamma_g2: G2Affine::one(), + delta_g1: G1Affine::one(), + delta_g2: G2Affine::one(), + ic: ic.into_iter().map(|e| e.into_affine()).collect() + }; + + let params = Parameters { + vk: vk, + h: Arc::new(h), + l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + + // Filter points at infinity away from A/B queries + a: Arc::new(a_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + }; + + let h = { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); - pub fn read( - mut reader: R, - checked: bool - ) -> io::Result - { - let params = Parameters::read(&mut reader, checked)?; + params.write(&mut sink).unwrap(); - let mut cs_hash = [0u8; 64]; - reader.read_exact(&mut cs_hash)?; + sink.into_hash() + }; - let contributions_len = reader.read_u32::()? as usize; - - let mut contributions = vec![]; - for _ in 0..contributions_len { - contributions.push(PublicKey::read(&mut reader)?); - } + let mut cs_hash = [0; 64]; + cs_hash.copy_from_slice(h.as_ref()); Ok(MPCParameters { - params, cs_hash, contributions + params: params, + cs_hash: cs_hash, + contributions: vec![] }) } - pub fn params(&self) -> &Parameters { + /// Get the underlying Groth16 `Parameters` + pub fn get_params(&self) -> &Parameters { &self.params } - pub fn transform( + /// Contributes some randomness to the parameters. Only one + /// contributor needs to destroy their `PrivateKey` to keep + /// the parameters secure. See `keypair()` for creating + /// keypairs. + /// + /// This function returns a "hash" that is bound to the + /// contribution. Contributors can use this hash to make + /// sure their contribution is in the final parameters, by + /// checking to see if it appears in the output of + /// `MPCParameters::verify`. + pub fn contribute( &mut self, pubkey: &PublicKey, privkey: &PrivateKey @@ -592,6 +771,7 @@ impl MPCParameters { self.contributions.push(pubkey.clone()); + // Calculate the hash of the public key and return it { let sink = io::sink(); let mut sink = HashWriter::new(sink); @@ -603,12 +783,17 @@ impl MPCParameters { } } + /// Verify the correctness of the parameters, given a circuit + /// instance. This will return all of the hashes that + /// contributors obtained when they ran + /// `MPCParameters::contribute`, for ensuring that contributions + /// exist in the final parameters. pub fn verify>( &self, circuit: C ) -> Result, ()> { - let initial_params = new_parameters(circuit).map_err(|_| ())?; + let initial_params = MPCParameters::new(circuit).map_err(|_| ())?; // H/L will change, but should have same length if initial_params.params.h.len() != self.params.h.len() { @@ -732,8 +917,51 @@ impl MPCParameters { Ok(result) } + + /// Serialize these parameters. The serialized parameters + /// can be read by bellman as Groth16 `Parameters`. + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + self.params.write(&mut writer)?; + writer.write_all(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + + Ok(()) + } + + /// Deserialize these parameters. + pub fn read( + mut reader: R, + checked: bool + ) -> io::Result + { + let params = Parameters::read(&mut reader, checked)?; + + let mut cs_hash = [0u8; 64]; + reader.read_exact(&mut cs_hash)?; + + let contributions_len = reader.read_u32::()? as usize; + + let mut contributions = vec![]; + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut reader)?); + } + + Ok(MPCParameters { + params, cs_hash, contributions + }) + } } +/// This allows others to verify that you contributed. The hash produced +/// by `MPCParameters::contribute` is just a BLAKE2b hash of this object. #[derive(Clone)] pub struct PublicKey { /// This is the delta (in G1) after the transformation, kept so that we @@ -755,7 +983,7 @@ pub struct PublicKey { } impl PublicKey { - pub fn write( + fn write( &self, mut writer: W ) -> io::Result<()> @@ -769,7 +997,7 @@ impl PublicKey { Ok(()) } - pub fn read( + fn read( mut reader: R ) -> io::Result { @@ -823,11 +1051,16 @@ impl PartialEq for PublicKey { } } -pub fn verify_transform>( +/// Verify a contribution, given the old parameters and +/// the new parameters. This is basically a wrapper around +/// `MPCParameters::verify` which just checks that a new +/// contribution was added and none of the existing +/// contributions were changed. +pub fn verify_contribution>( circuit: C, before: &MPCParameters, after: &MPCParameters -) -> Result, ()> +) -> Result<[u8; 64], ()> { // Transformation involves a single new object if after.contributions.len() != (before.contributions.len() + 1) { @@ -839,7 +1072,7 @@ pub fn verify_transform>( return Err(()); } - after.verify(circuit) + after.verify(circuit).map(|v| *v.last().unwrap()) } /// Checks if pairs have the same ratio. @@ -912,10 +1145,15 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) (s, sx) } +/// This needs to be destroyed by at least one participant +/// for the final parameters to be secure. pub struct PrivateKey { delta: Fr } +/// Compute a keypair, given the current parameters. Keypairs +/// cannot be reused for multiple contributions or contributions +/// in different parameters. pub fn keypair( rng: &mut R, current: &MPCParameters, @@ -982,7 +1220,7 @@ fn hash_to_g2(mut digest: &[u8]) -> G2 } /// Abstraction over a writer which hashes the data being written. -pub struct HashWriter { +struct HashWriter { writer: W, hasher: Blake2b } @@ -1026,3 +1264,20 @@ impl Write for HashWriter { self.writer.flush() } } + +/// This is a cheap helper utility that exists purely +/// because Rust still doesn't have type-level integers +/// and so doesn't implement `PartialEq` for `[T; 64]` +pub fn contains_contribution( + contributions: &[[u8; 64]], + my_contribution: &[u8; 64] +) -> bool +{ + for contrib in contributions { + if &contrib[..] == &my_contribution[..] { + return true + } + } + + return false +} From 54e210b03fae8810c4f2197beb91f4eb3564aaa3 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 7 Apr 2018 20:56:28 -0600 Subject: [PATCH 07/40] Fix doc comment --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 25f103a24..c3f2c8a19 100644 --- a/lib.rs +++ b/lib.rs @@ -404,7 +404,7 @@ impl PartialEq for MPCParameters { impl MPCParameters { /// Create new Groth16 parameters (compatible with bellman) for a /// given circuit. The resulting parameters are unsafe to use - /// until there are contributions (see `transform`). + /// until there are contributions (see `contribute()`). pub fn new( circuit: C, ) -> Result From a28528559eb91c18e5ac8ef3161d522b5243b9ef Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 8 Apr 2018 13:49:01 -0600 Subject: [PATCH 08/40] Simplify API and improve performance --- lib.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 13 deletions(-) diff --git a/lib.rs b/lib.rs index c3f2c8a19..345ff2634 100644 --- a/lib.rs +++ b/lib.rs @@ -708,12 +708,14 @@ impl MPCParameters { /// sure their contribution is in the final parameters, by /// checking to see if it appears in the output of /// `MPCParameters::verify`. - pub fn contribute( + pub fn contribute( &mut self, - pubkey: &PublicKey, - privkey: &PrivateKey + rng: &mut R ) -> [u8; 64] { + // Generate a keypair + let (pubkey, privkey) = keypair(rng, self); + fn batch_exp(bases: &mut [C], coeff: C::Scalar) { let coeff = coeff.into_repr(); @@ -963,7 +965,7 @@ impl MPCParameters { /// This allows others to verify that you contributed. The hash produced /// by `MPCParameters::contribute` is just a BLAKE2b hash of this object. #[derive(Clone)] -pub struct PublicKey { +struct PublicKey { /// This is the delta (in G1) after the transformation, kept so that we /// can check correctness of the public keys without having the entire /// interstitial parameters for each contribution. @@ -1052,12 +1054,8 @@ impl PartialEq for PublicKey { } /// Verify a contribution, given the old parameters and -/// the new parameters. This is basically a wrapper around -/// `MPCParameters::verify` which just checks that a new -/// contribution was added and none of the existing -/// contributions were changed. -pub fn verify_contribution>( - circuit: C, +/// the new parameters. Returns the hash of the contribution. +pub fn verify_contribution( before: &MPCParameters, after: &MPCParameters ) -> Result<[u8; 64], ()> @@ -1072,7 +1070,119 @@ pub fn verify_contribution>( return Err(()); } - after.verify(circuit).map(|v| *v.last().unwrap()) + // H/L will change, but should have same length + if before.params.h.len() != after.params.h.len() { + return Err(()); + } + if before.params.l.len() != after.params.l.len() { + return Err(()); + } + + // A/B_G1/B_G2 doesn't change at all + if before.params.a != after.params.a { + return Err(()); + } + if before.params.b_g1 != after.params.b_g1 { + return Err(()); + } + if before.params.b_g2 != after.params.b_g2 { + return Err(()); + } + + // alpha/beta/gamma don't change + if before.params.vk.alpha_g1 != after.params.vk.alpha_g1 { + return Err(()); + } + if before.params.vk.beta_g1 != after.params.vk.beta_g1 { + return Err(()); + } + if before.params.vk.beta_g2 != after.params.vk.beta_g2 { + return Err(()); + } + if before.params.vk.gamma_g2 != after.params.vk.gamma_g2 { + return Err(()); + } + + // IC shouldn't change, as gamma doesn't change + if before.params.vk.ic != after.params.vk.ic { + return Err(()); + } + + // cs_hash should be the same + if &before.cs_hash[..] != &after.cs_hash[..] { + return Err(()); + } + + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + sink.write_all(&before.cs_hash[..]).unwrap(); + + for pubkey in &before.contributions { + sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + } + + let pubkey = after.contributions.last().unwrap(); + sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); + sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + + let h = sink.into_hash(); + + // The transcript must be consistent + if &pubkey.transcript[..] != h.as_ref() { + return Err(()); + } + + let r = hash_to_g2(h.as_ref()).into_affine(); + + // Check the signature of knowledge + if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { + return Err(()); + } + + // Check the change from the old delta is consistent + if !same_ratio( + (before.params.vk.delta_g1, pubkey.delta_after), + (r, pubkey.r_delta) + ) { + return Err(()); + } + + // Current parameters should have consistent delta in G1 + if pubkey.delta_after != after.params.vk.delta_g1 { + return Err(()); + } + + // Current parameters should have consistent delta in G2 + if !same_ratio( + (G1Affine::one(), pubkey.delta_after), + (G2Affine::one(), after.params.vk.delta_g2) + ) { + return Err(()); + } + + // H and L queries should be updated with delta^-1 + if !same_ratio( + merge_pairs(&before.params.h, &after.params.h), + (after.params.vk.delta_g2, before.params.vk.delta_g2) // reversed for inverse + ) { + return Err(()); + } + + if !same_ratio( + merge_pairs(&before.params.l, &after.params.l), + (after.params.vk.delta_g2, before.params.vk.delta_g2) // reversed for inverse + ) { + return Err(()); + } + + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + pubkey.write(&mut sink).unwrap(); + let h = sink.into_hash(); + let mut response = [0u8; 64]; + response.copy_from_slice(h.as_ref()); + + Ok(response) } /// Checks if pairs have the same ratio. @@ -1147,14 +1257,14 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) /// This needs to be destroyed by at least one participant /// for the final parameters to be secure. -pub struct PrivateKey { +struct PrivateKey { delta: Fr } /// Compute a keypair, given the current parameters. Keypairs /// cannot be reused for multiple contributions or contributions /// in different parameters. -pub fn keypair( +fn keypair( rng: &mut R, current: &MPCParameters, ) -> (PublicKey, PrivateKey) From 3b50f1bcbf27e26a2ed7148a4d4a578c2e63bd4d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 8 Apr 2018 14:03:41 -0600 Subject: [PATCH 09/40] Add some `expect`s. --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index 345ff2634..c76dd0a10 100644 --- a/lib.rs +++ b/lib.rs @@ -760,7 +760,7 @@ impl MPCParameters { } } - let delta_inv = privkey.delta.inverse().unwrap(); + let delta_inv = privkey.delta.inverse().expect("nonzero"); let mut l = (&self.params.l[..]).to_vec(); let mut h = (&self.params.h[..]).to_vec(); batch_exp(&mut l, delta_inv); From c32f1adb3866f4b696b3a2ff56907df376e39640 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 8 Apr 2018 14:06:20 -0600 Subject: [PATCH 10/40] Write entire pubkeys for signatures. --- lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib.rs b/lib.rs index c76dd0a10..c7375d874 100644 --- a/lib.rs +++ b/lib.rs @@ -852,7 +852,7 @@ impl MPCParameters { our_sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); our_sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); - sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + pubkey.write(&mut sink).unwrap(); let h = our_sink.into_hash(); @@ -1118,7 +1118,7 @@ pub fn verify_contribution( sink.write_all(&before.cs_hash[..]).unwrap(); for pubkey in &before.contributions { - sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + pubkey.write(&mut sink).unwrap(); } let pubkey = after.contributions.last().unwrap(); @@ -1276,14 +1276,14 @@ fn keypair( let s = G1::rand(rng).into_affine(); let s_delta = s.mul(delta).into_affine(); - // H(cs_hash | | s | s_delta) + // H(cs_hash | | s | s_delta) let h = { let sink = io::sink(); let mut sink = HashWriter::new(sink); sink.write_all(¤t.cs_hash[..]).unwrap(); for pubkey in ¤t.contributions { - sink.write_all(pubkey.delta_after.into_uncompressed().as_ref()).unwrap(); + pubkey.write(&mut sink).unwrap(); } sink.write_all(s.into_uncompressed().as_ref()).unwrap(); sink.write_all(s_delta.into_uncompressed().as_ref()).unwrap(); From 37df9d479b51d3edfe4aeca4424e60532fbcce9d Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 8 Apr 2018 14:12:43 -0600 Subject: [PATCH 11/40] Fix documentation --- lib.rs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/lib.rs b/lib.rs index c7375d874..aae5552e9 100644 --- a/lib.rs +++ b/lib.rs @@ -166,23 +166,17 @@ //! parameters. //! //! ```rust,ignore -//! // Create a random keypair for our parameters -//! let (pubkey, privkey) = phase2::keypair(rng, ¶ms); -//! -//! // Contribute to the parameters. Remember this hash, it's -//! // how we know our contribution is in the parameters! -//! let hash = params.contribute(&pubkey, &privkey); -//! -//! // Throw away the private key! -//! drop(privkey); +//! // Contribute randomness to the parameters. Remember this hash, +//! // it's how we know our contribution is in the parameters! +//! let hash = params.contribute(rng); //! ``` //! -//! These parameters are now secure to use, so long as you destroyed -//! the privkey. That may not be convincing to others, so let them +//! These parameters are now secure to use, so long as you weren't +//! malicious. That may not be convincing to others, so let them //! contribute randomness too! `params` can be serialized and sent //! elsewhere, where they can do the same thing and send new -//! parameters back to you. Only one person needs to destroy the -//! `privkey` for the final parameters to be secure. +//! parameters back to you. Only one person needs to be honest for +//! the final parameters to be secure. //! //! Once you're done setting up the parameters, you can verify the //! parameters: @@ -699,9 +693,8 @@ impl MPCParameters { } /// Contributes some randomness to the parameters. Only one - /// contributor needs to destroy their `PrivateKey` to keep - /// the parameters secure. See `keypair()` for creating - /// keypairs. + /// contributor needs to be honest for the parameters to be + /// secure. /// /// This function returns a "hash" that is bound to the /// contribution. Contributors can use this hash to make @@ -938,7 +931,9 @@ impl MPCParameters { Ok(()) } - /// Deserialize these parameters. + /// Deserialize these parameters. If `checked` is false, + /// we won't perform curve validity and group order + /// checks. pub fn read( mut reader: R, checked: bool From 377dcecaa04048cc61c9df7fdf6778cd1aac4eb4 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 8 Apr 2018 15:02:19 -0600 Subject: [PATCH 12/40] Switch to simpler BLAKE2b implementation. --- lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib.rs b/lib.rs index aae5552e9..a9c7a528d 100644 --- a/lib.rs +++ b/lib.rs @@ -199,15 +199,11 @@ extern crate pairing; extern crate bellman; extern crate rand; extern crate byteorder; -extern crate blake2; +extern crate blake2_rfc; extern crate num_cpus; extern crate crossbeam; -extern crate generic_array; -extern crate typenum; -use blake2::{Blake2b, Digest}; -use generic_array::GenericArray; -use typenum::consts::U64; +use blake2_rfc::blake2b::Blake2b; use byteorder::{ BigEndian, @@ -1344,13 +1340,15 @@ impl HashWriter { pub fn new(writer: W) -> Self { HashWriter { writer: writer, - hasher: Blake2b::default() + hasher: Blake2b::new(64) } } /// Destroy this writer and return the hash of what was written. - pub fn into_hash(self) -> GenericArray { - self.hasher.result() + pub fn into_hash(self) -> [u8; 64] { + let mut tmp = [0u8; 64]; + tmp.copy_from_slice(self.hasher.finalize().as_ref()); + tmp } } @@ -1359,7 +1357,7 @@ impl Write for HashWriter { let bytes = self.writer.write(buf)?; if bytes > 0 { - self.hasher.input(&buf[0..bytes]); + self.hasher.update(&buf[0..bytes]); } Ok(bytes) From c5f27b5803421fa494cd76d7d09c7a155fca9fc0 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 12 Apr 2018 22:27:49 -0600 Subject: [PATCH 13/40] Update link for Powers of Tau. --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index a9c7a528d..e8de13021 100644 --- a/lib.rs +++ b/lib.rs @@ -159,7 +159,7 @@ //! //! The first time you try this, it will try to read a file like //! `phase1radix2m2` from the current directory. You need to grab -//! that from the Powers of Tau. +//! that from the [Powers of Tau](https://lists.z.cash.foundation/pipermail/zapps-wg/2018/000362.html). //! //! These parameters are not safe to use; false proofs can be //! created for them. Let's contribute some randomness to these From b73776fc93ef15327259ad22a83cd13f5dd2faea Mon Sep 17 00:00:00 2001 From: Peter van Nostrand Date: Sun, 22 Apr 2018 14:49:22 -0400 Subject: [PATCH 14/40] Fixed link to pairing library in docs. --- lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib.rs b/lib.rs index e8de13021..e0916039d 100644 --- a/lib.rs +++ b/lib.rs @@ -3,7 +3,7 @@ //! ## Make your circuit //! //! Grab the [`bellman`](https://github.com/ebfull/bellman) and -//! [`pairing`](https://github.com/ebfull/bellman) crates. Bellman +//! [`pairing`](https://github.com/ebfull/pairing) crates. Bellman //! provides a trait called `Circuit`, which you must implement //! for your computation. //! From ae675143932b6b4b9de4353447a4c6794a2b3259 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Mar 2019 11:26:39 +0100 Subject: [PATCH 15/40] feat: depend on the latest bellman and pairing versions --- lib.rs | 480 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 233 insertions(+), 247 deletions(-) diff --git a/lib.rs b/lib.rs index e0916039d..ed44e8e5a 100644 --- a/lib.rs +++ b/lib.rs @@ -13,18 +13,20 @@ //! ```rust //! extern crate pairing; //! extern crate bellman; -//! -//! use pairing::{Engine, Field}; +//! extern crate ff; +//! +//! use pairing::Engine; +//! use ff::Field; //! use bellman::{ //! Circuit, //! ConstraintSystem, //! SynthesisError, //! }; -//! +//! //! struct CubeRoot { //! cube_root: Option //! } -//! +//! //! impl Circuit for CubeRoot { //! fn synthesize>( //! self, @@ -35,22 +37,22 @@ //! let root = cs.alloc(|| "root", || { //! self.cube_root.ok_or(SynthesisError::AssignmentMissing) //! })?; -//! +//! //! // Witness the square of the cube root //! let square = cs.alloc(|| "square", || { //! self.cube_root //! .ok_or(SynthesisError::AssignmentMissing) //! .map(|mut root| {root.square(); root }) //! })?; -//! +//! //! // Enforce that `square` is root^2 //! cs.enforce( //! || "squaring", //! |lc| lc + root, //! |lc| lc + root, -//! |lc| lc + square +//! |lc| lc + square //! ); -//! +//! //! // Witness the cube, as a public input //! let cube = cs.alloc_input(|| "cube", || { //! self.cube_root @@ -62,7 +64,7 @@ //! tmp //! }) //! })?; -//! +//! //! // Enforce that `cube` is root^3 //! // i.e. that `cube` is `root` * `square` //! cs.enforce( @@ -83,7 +85,7 @@ //! let's create some parameters and make some proofs. //! //! ```rust,ignore -//! extern crate rand; +//! extern crate rand; //! //! use pairing::bls12_381::{Bls12, Fr}; //! use bellman::groth16::{ @@ -93,24 +95,24 @@ //! verify_proof //! }; //! use rand::{OsRng, Rand}; -//! +//! //! let rng = &mut OsRng::new(); -//! +//! //! // Create public parameters for our circuit //! let params = { //! let circuit = CubeRoot:: { //! cube_root: None //! }; -//! +//! //! generate_random_parameters::( //! circuit, //! rng //! ).unwrap() //! }; -//! +//! //! // Prepare the verifying key for verification //! let pvk = prepare_verifying_key(¶ms.vk); -//! +//! //! // Let's start making proofs! //! for _ in 0..50 { //! // Verifier picks a cube in the field. @@ -119,7 +121,7 @@ //! let mut cube = root; //! cube.square(); //! cube.mul_assign(&root); -//! +//! //! // Prover gets the cube, figures out the cube //! // root, and makes the proof: //! let proof = create_random_proof( @@ -127,7 +129,7 @@ //! cube_root: Some(root) //! }, ¶ms, rng //! ).unwrap(); -//! +//! //! // Verifier checks the proof against the cube //! assert!(verify_proof(&pvk, &proof, &[cube]).unwrap()); //! } @@ -160,7 +162,7 @@ //! The first time you try this, it will try to read a file like //! `phase1radix2m2` from the current directory. You need to grab //! that from the [Powers of Tau](https://lists.z.cash.foundation/pipermail/zapps-wg/2018/000362.html). -//! +//! //! These parameters are not safe to use; false proofs can be //! created for them. Let's contribute some randomness to these //! parameters. @@ -195,77 +197,39 @@ //! `params.params()`, so that you can interact with the bellman APIs //! just as before. -extern crate pairing; extern crate bellman; -extern crate rand; -extern crate byteorder; extern crate blake2_rfc; -extern crate num_cpus; +extern crate byteorder; extern crate crossbeam; +extern crate num_cpus; +extern crate pairing; +extern crate rand; +extern crate ff; use blake2_rfc::blake2b::Blake2b; -use byteorder::{ - BigEndian, - ReadBytesExt, - WriteBytesExt -}; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use std::{ - io::{ - self, - Read, - Write, - BufReader - }, - fs::{ - File - }, - sync::{ - Arc - } + fs::File, + io::{self, BufReader, Read, Write}, + sync::Arc, }; use pairing::{ - Engine, - PrimeField, - Field, - EncodedPoint, - CurveAffine, - CurveProjective, - Wnaf, - bls12_381::{ - Bls12, - Fr, - G1, - G2, - G1Affine, - G1Uncompressed, - G2Affine, - G2Uncompressed - } + bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, + CurveAffine, CurveProjective, EncodedPoint, Wnaf, Engine, }; +use ff::{Field, PrimeField}; + use bellman::{ - Circuit, - SynthesisError, - Variable, - Index, - ConstraintSystem, - LinearCombination, - groth16::{ - Parameters, - VerifyingKey - }, - multicore::Worker + groth16::{Parameters, VerifyingKey}, + multicore::Worker, + Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable, }; -use rand::{ - Rng, - Rand, - ChaChaRng, - SeedableRng -}; +use rand::{ChaChaRng, Rand, Rng, SeedableRng}; /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. @@ -278,18 +242,17 @@ struct KeypairAssembly { ct_inputs: Vec>, at_aux: Vec>, bt_aux: Vec>, - ct_aux: Vec> + ct_aux: Vec>, } impl ConstraintSystem for KeypairAssembly { type Root = Self; - fn alloc( - &mut self, - _: A, - _: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + fn alloc(&mut self, _: A, _: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, { // There is no assignment, so we don't even invoke the // function for obtaining one. @@ -304,12 +267,11 @@ impl ConstraintSystem for KeypairAssembly { Ok(Variable::new_unchecked(Index::Aux(index))) } - fn alloc_input( - &mut self, - _: A, - _: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + fn alloc_input(&mut self, _: A, _: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, { // There is no assignment, so we don't even invoke the // function for obtaining one. @@ -324,48 +286,59 @@ impl ConstraintSystem for KeypairAssembly { Ok(Variable::new_unchecked(Index::Input(index))) } - fn enforce( - &mut self, - _: A, - a: LA, - b: LB, - c: LC - ) - where A: FnOnce() -> AR, AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination + fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, { fn eval( l: LinearCombination, inputs: &mut [Vec<(E::Fr, usize)>], aux: &mut [Vec<(E::Fr, usize)>], - this_constraint: usize - ) - { + this_constraint: usize, + ) { for &(var, coeff) in l.as_ref() { match var.get_unchecked() { Index::Input(id) => inputs[id].push((coeff, this_constraint)), - Index::Aux(id) => aux[id].push((coeff, this_constraint)) + Index::Aux(id) => aux[id].push((coeff, this_constraint)), } } } - eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); - eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); - eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + eval( + a(LinearCombination::zero()), + &mut self.at_inputs, + &mut self.at_aux, + self.num_constraints, + ); + eval( + b(LinearCombination::zero()), + &mut self.bt_inputs, + &mut self.bt_aux, + self.num_constraints, + ); + eval( + c(LinearCombination::zero()), + &mut self.ct_inputs, + &mut self.ct_aux, + self.num_constraints, + ); self.num_constraints += 1; } fn push_namespace(&mut self, _: N) - where NR: Into, N: FnOnce() -> NR + where + NR: Into, + N: FnOnce() -> NR, { // Do nothing; we don't care about namespaces in this context. } - fn pop_namespace(&mut self) - { + fn pop_namespace(&mut self) { // Do nothing; we don't care about namespaces in this context. } @@ -380,14 +353,14 @@ impl ConstraintSystem for KeypairAssembly { pub struct MPCParameters { params: Parameters, cs_hash: [u8; 64], - contributions: Vec + contributions: Vec, } impl PartialEq for MPCParameters { fn eq(&self, other: &MPCParameters) -> bool { - self.params == other.params && - &self.cs_hash[..] == &other.cs_hash[..] && - self.contributions == other.contributions + self.params == other.params + && &self.cs_hash[..] == &other.cs_hash[..] + && self.contributions == other.contributions } } @@ -395,10 +368,9 @@ impl MPCParameters { /// Create new Groth16 parameters (compatible with bellman) for a /// given circuit. The resulting parameters are unsafe to use /// until there are contributions (see `contribute()`). - pub fn new( - circuit: C, - ) -> Result - where C: Circuit + pub fn new(circuit: C) -> Result + where + C: Circuit, { let mut assembly = KeypairAssembly { num_inputs: 0, @@ -409,7 +381,7 @@ impl MPCParameters { ct_inputs: vec![], at_aux: vec![], bt_aux: vec![], - ct_aux: vec![] + ct_aux: vec![], }; // Allocate the "one" input variable @@ -421,7 +393,8 @@ impl MPCParameters { // Input constraints to ensure full density of IC query // x * 0 = 0 for i in 0..assembly.num_inputs { - assembly.enforce(|| "", + assembly.enforce( + || "", |lc| lc + Variable::new_unchecked(Index::Input(i)), |lc| lc, |lc| lc, @@ -437,7 +410,7 @@ impl MPCParameters { // Powers of Tau ceremony can't support more than 2^21 if exp > 21 { - return Err(SynthesisError::PolynomialDegreeTooLarge) + return Err(SynthesisError::PolynomialDegreeTooLarge); } } @@ -455,12 +428,17 @@ impl MPCParameters { reader.read_exact(repr.as_mut())?; 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) - }) + .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 BufReader| -> io::Result { @@ -468,12 +446,17 @@ impl MPCParameters { reader.read_exact(repr.as_mut())?; 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) - }) + .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 alpha = read_g1(f)?; @@ -538,9 +521,8 @@ impl MPCParameters { ext: &mut [G1], // Worker - worker: &Worker - ) - { + worker: &Worker, + ) { // Sanity check assert_eq!(a_g1.len(), at.len()); assert_eq!(a_g1.len(), bt.len()); @@ -551,8 +533,8 @@ impl MPCParameters { // Evaluate polynomials in multiple threads worker.scope(a_g1.len(), |scope, chunk| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in - a_g1.chunks_mut(chunk) + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .chunks_mut(chunk) .zip(b_g1.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk)) .zip(ext.chunks_mut(chunk)) @@ -566,8 +548,8 @@ impl MPCParameters { let beta_coeffs_g1 = beta_coeffs_g1.clone(); scope.spawn(move || { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in - a_g1.iter_mut() + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .iter_mut() .zip(b_g1.iter_mut()) .zip(b_g2.iter_mut()) .zip(ext.iter_mut()) @@ -616,7 +598,7 @@ impl MPCParameters { &mut b_g1[0..assembly.num_inputs], &mut b_g2[0..assembly.num_inputs], &mut ic, - &worker + &worker, ); // Evaluate for auxillary variables. @@ -632,7 +614,7 @@ impl MPCParameters { &mut b_g1[assembly.num_inputs..], &mut b_g2[assembly.num_inputs..], &mut l, - &worker + &worker, ); // Don't allow any elements be unconstrained, so that @@ -650,7 +632,7 @@ impl MPCParameters { gamma_g2: G2Affine::one(), delta_g1: G1Affine::one(), delta_g2: G2Affine::one(), - ic: ic.into_iter().map(|e| e.into_affine()).collect() + ic: ic.into_iter().map(|e| e.into_affine()).collect(), }; let params = Parameters { @@ -659,9 +641,24 @@ impl MPCParameters { l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), // Filter points at infinity away from A/B queries - a: Arc::new(a_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), - b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), - b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + a: Arc::new( + a_g1.into_iter() + .filter(|e| !e.is_zero()) + .map(|e| e.into_affine()) + .collect(), + ), + b_g1: Arc::new( + b_g1.into_iter() + .filter(|e| !e.is_zero()) + .map(|e| e.into_affine()) + .collect(), + ), + b_g2: Arc::new( + b_g2.into_iter() + .filter(|e| !e.is_zero()) + .map(|e| e.into_affine()) + .collect(), + ), }; let h = { @@ -679,7 +676,7 @@ impl MPCParameters { Ok(MPCParameters { params: params, cs_hash: cs_hash, - contributions: vec![] + contributions: vec![], }) } @@ -697,11 +694,7 @@ impl MPCParameters { /// sure their contribution is in the final parameters, by /// checking to see if it appears in the output of /// `MPCParameters::verify`. - pub fn contribute( - &mut self, - rng: &mut R - ) -> [u8; 64] - { + pub fn contribute(&mut self, rng: &mut R) -> [u8; 64] { // Generate a keypair let (pubkey, privkey) = keypair(rng, self); @@ -718,15 +711,14 @@ impl MPCParameters { // Perform wNAF over multiple cores, placing results into `projective`. crossbeam::scope(|scope| { - for (bases, projective) in bases.chunks_mut(chunk_size) - .zip(projective.chunks_mut(chunk_size)) + for (bases, projective) in bases + .chunks_mut(chunk_size) + .zip(projective.chunks_mut(chunk_size)) { scope.spawn(move || { let mut wnaf = Wnaf::new(); - for (base, projective) in bases.iter_mut() - .zip(projective.iter_mut()) - { + for (base, projective) in bases.iter_mut().zip(projective.iter_mut()) { *projective = wnaf.base(base.into_projective(), 1).scalar(coeff); } }); @@ -735,8 +727,7 @@ impl MPCParameters { // Perform batch normalization crossbeam::scope(|scope| { - for projective in projective.chunks_mut(chunk_size) - { + for projective in projective.chunks_mut(chunk_size) { scope.spawn(move || { C::Projective::batch_normalization(projective); }); @@ -779,11 +770,7 @@ impl MPCParameters { /// contributors obtained when they ran /// `MPCParameters::contribute`, for ensuring that contributions /// exist in the final parameters. - pub fn verify>( - &self, - circuit: C - ) -> Result, ()> - { + pub fn verify>(&self, circuit: C) -> Result, ()> { let initial_params = MPCParameters::new(circuit).map_err(|_| ())?; // H/L will change, but should have same length @@ -838,8 +825,12 @@ impl MPCParameters { for pubkey in &self.contributions { let mut our_sink = sink.clone(); - our_sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); - our_sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + our_sink + .write_all(pubkey.s.into_uncompressed().as_ref()) + .unwrap(); + our_sink + .write_all(pubkey.s_delta.into_uncompressed().as_ref()) + .unwrap(); pubkey.write(&mut sink).unwrap(); @@ -858,10 +849,7 @@ impl MPCParameters { } // Check the change from the old delta is consistent - if !same_ratio( - (current_delta, pubkey.delta_after), - (r, pubkey.r_delta) - ) { + if !same_ratio((current_delta, pubkey.delta_after), (r, pubkey.r_delta)) { return Err(()); } @@ -886,7 +874,7 @@ impl MPCParameters { // Current parameters should have consistent delta in G2 if !same_ratio( (G1Affine::one(), current_delta), - (G2Affine::one(), self.params.vk.delta_g2) + (G2Affine::one(), self.params.vk.delta_g2), ) { return Err(()); } @@ -894,14 +882,14 @@ impl MPCParameters { // H and L queries should be updated with delta^-1 if !same_ratio( merge_pairs(&initial_params.params.h, &self.params.h), - (self.params.vk.delta_g2, G2Affine::one()) // reversed for inverse + (self.params.vk.delta_g2, G2Affine::one()), // reversed for inverse ) { return Err(()); } if !same_ratio( merge_pairs(&initial_params.params.l, &self.params.l), - (self.params.vk.delta_g2, G2Affine::one()) // reversed for inverse + (self.params.vk.delta_g2, G2Affine::one()), // reversed for inverse ) { return Err(()); } @@ -911,11 +899,7 @@ impl MPCParameters { /// Serialize these parameters. The serialized parameters /// can be read by bellman as Groth16 `Parameters`. - pub fn write( - &self, - mut writer: W - ) -> io::Result<()> - { + pub fn write(&self, mut writer: W) -> io::Result<()> { self.params.write(&mut writer)?; writer.write_all(&self.cs_hash)?; @@ -930,11 +914,7 @@ impl MPCParameters { /// Deserialize these parameters. If `checked` is false, /// we won't perform curve validity and group order /// checks. - pub fn read( - mut reader: R, - checked: bool - ) -> io::Result - { + pub fn read(mut reader: R, checked: bool) -> io::Result { let params = Parameters::read(&mut reader, checked)?; let mut cs_hash = [0u8; 64]; @@ -948,7 +928,9 @@ impl MPCParameters { } Ok(MPCParameters { - params, cs_hash, contributions + params, + cs_hash, + contributions, }) } } @@ -976,11 +958,7 @@ struct PublicKey { } impl PublicKey { - fn write( - &self, - mut writer: W - ) -> io::Result<()> - { + fn write(&self, mut writer: W) -> io::Result<()> { writer.write_all(self.delta_after.into_uncompressed().as_ref())?; writer.write_all(self.s.into_uncompressed().as_ref())?; writer.write_all(self.s_delta.into_uncompressed().as_ref())?; @@ -990,67 +968,84 @@ impl PublicKey { Ok(()) } - fn read( - mut reader: R - ) -> io::Result - { + fn read(mut reader: R) -> io::Result { let mut g1_repr = G1Uncompressed::empty(); let mut g2_repr = G2Uncompressed::empty(); reader.read_exact(g1_repr.as_mut())?; - let delta_after = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let delta_after = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if delta_after.is_zero() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "point at infinity", + )); } reader.read_exact(g1_repr.as_mut())?; - let s = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let s = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if s.is_zero() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "point at infinity", + )); } reader.read_exact(g1_repr.as_mut())?; - let s_delta = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let s_delta = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if s_delta.is_zero() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "point at infinity", + )); } reader.read_exact(g2_repr.as_mut())?; - let r_delta = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let r_delta = g2_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if r_delta.is_zero() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "point at infinity", + )); } let mut transcript = [0u8; 64]; reader.read_exact(&mut transcript)?; Ok(PublicKey { - delta_after, s, s_delta, r_delta, transcript + delta_after, + s, + s_delta, + r_delta, + transcript, }) } } impl PartialEq for PublicKey { fn eq(&self, other: &PublicKey) -> bool { - self.delta_after == other.delta_after && - self.s == other.s && - self.s_delta == other.s_delta && - self.r_delta == other.r_delta && - &self.transcript[..] == &other.transcript[..] + self.delta_after == other.delta_after + && self.s == other.s + && self.s_delta == other.s_delta + && self.r_delta == other.r_delta + && &self.transcript[..] == &other.transcript[..] } } /// Verify a contribution, given the old parameters and /// the new parameters. Returns the hash of the contribution. -pub fn verify_contribution( - before: &MPCParameters, - after: &MPCParameters -) -> Result<[u8; 64], ()> -{ +pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Result<[u8; 64], ()> { // Transformation involves a single new object if after.contributions.len() != (before.contributions.len() + 1) { return Err(()); @@ -1113,8 +1108,10 @@ pub fn verify_contribution( } let pubkey = after.contributions.last().unwrap(); - sink.write_all(pubkey.s.into_uncompressed().as_ref()).unwrap(); - sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + sink.write_all(pubkey.s.into_uncompressed().as_ref()) + .unwrap(); + sink.write_all(pubkey.s_delta.into_uncompressed().as_ref()) + .unwrap(); let h = sink.into_hash(); @@ -1133,7 +1130,7 @@ pub fn verify_contribution( // Check the change from the old delta is consistent if !same_ratio( (before.params.vk.delta_g1, pubkey.delta_after), - (r, pubkey.r_delta) + (r, pubkey.r_delta), ) { return Err(()); } @@ -1146,7 +1143,7 @@ pub fn verify_contribution( // Current parameters should have consistent delta in G2 if !same_ratio( (G1Affine::one(), pubkey.delta_after), - (G2Affine::one(), after.params.vk.delta_g2) + (G2Affine::one(), after.params.vk.delta_g2), ) { return Err(()); } @@ -1154,14 +1151,14 @@ pub fn verify_contribution( // H and L queries should be updated with delta^-1 if !same_ratio( merge_pairs(&before.params.h, &after.params.h), - (after.params.vk.delta_g2, before.params.vk.delta_g2) // reversed for inverse + (after.params.vk.delta_g2, before.params.vk.delta_g2), // reversed for inverse ) { return Err(()); } if !same_ratio( merge_pairs(&before.params.l, &after.params.l), - (after.params.vk.delta_g2, before.params.vk.delta_g2) // reversed for inverse + (after.params.vk.delta_g2, before.params.vk.delta_g2), // reversed for inverse ) { return Err(()); } @@ -1177,11 +1174,7 @@ pub fn verify_contribution( } /// Checks if pairs have the same ratio. -fn same_ratio( - g1: (G1, G1), - g2: (G1::Pair, G1::Pair) -) -> bool -{ +fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> bool { g1.0.pairing_with(&g2.1) == g1.1.pairing_with(&g2.0) } @@ -1198,10 +1191,9 @@ fn same_ratio( /// e(g, (as)*r1 + (bs)*r2 + (cs)*r3) = e(g^s, a*r1 + b*r2 + c*r3) /// /// ... with high probability. -fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) -{ +fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { + use rand::thread_rng; use std::sync::{Arc, Mutex}; - use rand::{thread_rng}; assert_eq!(v1.len(), v2.len()); @@ -1249,17 +1241,13 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) /// This needs to be destroyed by at least one participant /// for the final parameters to be secure. struct PrivateKey { - delta: Fr + delta: Fr, } /// Compute a keypair, given the current parameters. Keypairs /// cannot be reused for multiple contributions or contributions /// in different parameters. -fn keypair( - rng: &mut R, - current: &MPCParameters, -) -> (PublicKey, PrivateKey) -{ +fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateKey) { // Sample random delta let delta: Fr = rng.gen(); @@ -1277,7 +1265,8 @@ fn keypair( pubkey.write(&mut sink).unwrap(); } sink.write_all(s.into_uncompressed().as_ref()).unwrap(); - sink.write_all(s_delta.into_uncompressed().as_ref()).unwrap(); + sink.write_all(s_delta.into_uncompressed().as_ref()) + .unwrap(); sink.into_hash() }; @@ -1297,24 +1286,25 @@ fn keypair( s: s, s_delta: s_delta, r_delta: r_delta, - transcript: transcript + transcript: transcript, }, - PrivateKey { - delta: delta - } + PrivateKey { delta: delta }, ) } /// Hashes to G2 using the first 32 bytes of `digest`. Panics if `digest` is less /// than 32 bytes. -fn hash_to_g2(mut digest: &[u8]) -> G2 -{ +fn hash_to_g2(mut digest: &[u8]) -> G2 { assert!(digest.len() >= 32); let mut seed = Vec::with_capacity(8); for _ in 0..8 { - seed.push(digest.read_u32::().expect("assertion above guarantees this to work")); + seed.push( + digest + .read_u32::() + .expect("assertion above guarantees this to work"), + ); } ChaChaRng::from_seed(&seed).gen() @@ -1323,14 +1313,14 @@ fn hash_to_g2(mut digest: &[u8]) -> G2 /// Abstraction over a writer which hashes the data being written. struct HashWriter { writer: W, - hasher: Blake2b + hasher: Blake2b, } impl Clone for HashWriter { fn clone(&self) -> HashWriter { HashWriter { writer: io::sink(), - hasher: self.hasher.clone() + hasher: self.hasher.clone(), } } } @@ -1340,7 +1330,7 @@ impl HashWriter { pub fn new(writer: W) -> Self { HashWriter { writer: writer, - hasher: Blake2b::new(64) + hasher: Blake2b::new(64), } } @@ -1371,16 +1361,12 @@ impl Write for HashWriter { /// This is a cheap helper utility that exists purely /// because Rust still doesn't have type-level integers /// and so doesn't implement `PartialEq` for `[T; 64]` -pub fn contains_contribution( - contributions: &[[u8; 64]], - my_contribution: &[u8; 64] -) -> bool -{ +pub fn contains_contribution(contributions: &[[u8; 64]], my_contribution: &[u8; 64]) -> bool { for contrib in contributions { if &contrib[..] == &my_contribution[..] { - return true + return true; } } - return false + return false; } From d7b83d98f1b4a703734b582a50a25fcdb3702736 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Mar 2019 11:29:12 +0100 Subject: [PATCH 16/40] feat: use faster blake2b_simd impl --- lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib.rs b/lib.rs index ed44e8e5a..c9134be45 100644 --- a/lib.rs +++ b/lib.rs @@ -198,7 +198,7 @@ //! just as before. extern crate bellman; -extern crate blake2_rfc; +extern crate blake2b_simd; extern crate byteorder; extern crate crossbeam; extern crate num_cpus; @@ -206,7 +206,7 @@ extern crate pairing; extern crate rand; extern crate ff; -use blake2_rfc::blake2b::Blake2b; +use blake2b_simd::State as Blake2b; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; @@ -1330,12 +1330,12 @@ impl HashWriter { pub fn new(writer: W) -> Self { HashWriter { writer: writer, - hasher: Blake2b::new(64), + hasher: Blake2b::new(), } } /// Destroy this writer and return the hash of what was written. - pub fn into_hash(self) -> [u8; 64] { + pub fn into_hash(mut self) -> [u8; 64] { let mut tmp = [0u8; 64]; tmp.copy_from_slice(self.hasher.finalize().as_ref()); tmp From 99e8647ccece58c78c09e4763580b32f66a5ab4c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Mar 2019 11:31:07 +0100 Subject: [PATCH 17/40] feat: update crossbeam --- lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib.rs b/lib.rs index c9134be45..88bb15651 100644 --- a/lib.rs +++ b/lib.rs @@ -710,12 +710,12 @@ impl MPCParameters { }; // Perform wNAF over multiple cores, placing results into `projective`. - crossbeam::scope(|scope| { + crossbeam::thread::scope(|scope| { for (bases, projective) in bases .chunks_mut(chunk_size) .zip(projective.chunks_mut(chunk_size)) { - scope.spawn(move || { + scope.spawn(move |_| { let mut wnaf = Wnaf::new(); for (base, projective) in bases.iter_mut().zip(projective.iter_mut()) { @@ -723,16 +723,16 @@ impl MPCParameters { } }); } - }); + }).unwrap(); // Perform batch normalization - crossbeam::scope(|scope| { + crossbeam::thread::scope(|scope| { for projective in projective.chunks_mut(chunk_size) { - scope.spawn(move || { + scope.spawn(move |_| { C::Projective::batch_normalization(projective); }); } - }); + }).unwrap(); // Turn it all back into affine points for (projective, affine) in projective.iter().zip(bases.iter_mut()) { @@ -1202,12 +1202,12 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { let s = Arc::new(Mutex::new(G::Projective::zero())); let sx = Arc::new(Mutex::new(G::Projective::zero())); - crossbeam::scope(|scope| { + crossbeam::thread::scope(|scope| { for (v1, v2) in v1.chunks(chunk).zip(v2.chunks(chunk)) { let s = s.clone(); let sx = sx.clone(); - scope.spawn(move || { + scope.spawn(move |_| { // We do not need to be overly cautious of the RNG // used for this check. let rng = &mut thread_rng(); @@ -1230,7 +1230,7 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { sx.lock().unwrap().add_assign(&local_sx); }); } - }); + }).unwrap(); let s = s.lock().unwrap().into_affine(); let sx = sx.lock().unwrap().into_affine(); From 83786093859ce13cece5a82dbb8f7456ffe2a196 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 20 Mar 2019 13:15:42 +0100 Subject: [PATCH 18/40] bump version --- lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib.rs b/lib.rs index 88bb15651..a8f4b04b2 100644 --- a/lib.rs +++ b/lib.rs @@ -547,7 +547,7 @@ impl MPCParameters { let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); let beta_coeffs_g1 = beta_coeffs_g1.clone(); - scope.spawn(move || { + scope.spawn(move |_| { for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 .iter_mut() .zip(b_g1.iter_mut()) @@ -580,7 +580,7 @@ impl MPCParameters { G1::batch_normalization(ext); }); } - }); + }).unwrap(); } let worker = Worker::new(); From f9adc781cf51c410471f0dd40bbc3a17e3909738 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 20 May 2019 14:54:35 +0200 Subject: [PATCH 19/40] refactor: rename phase2 -> phase21 --- lib.rs | 143 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 69 deletions(-) diff --git a/lib.rs b/lib.rs index a8f4b04b2..6bfe8ee63 100644 --- a/lib.rs +++ b/lib.rs @@ -2,8 +2,8 @@ //! //! ## Make your circuit //! -//! Grab the [`bellman`](https://github.com/ebfull/bellman) and -//! [`pairing`](https://github.com/ebfull/pairing) crates. Bellman +//! Grab the [`bellperson`](https://github.com/filecoin-project/bellman) and +//! [`paired`](https://github.com/filecoin-project/pairing) crates. Bellman //! provides a trait called `Circuit`, which you must implement //! for your computation. //! @@ -11,13 +11,13 @@ //! a field element. //! //! ```rust -//! extern crate pairing; -//! extern crate bellman; +//! extern crate paired; +//! extern crate bellperson; //! extern crate ff; //! -//! use pairing::Engine; +//! use paired::Engine; //! use ff::Field; -//! use bellman::{ +//! use bellperson::{ //! Circuit, //! ConstraintSystem, //! SynthesisError, @@ -87,8 +87,8 @@ //! ```rust,ignore //! extern crate rand; //! -//! use pairing::bls12_381::{Bls12, Fr}; -//! use bellman::groth16::{ +//! use paired::bls12_381::{Bls12, Fr}; +//! use bellperson::groth16::{ //! generate_random_parameters, //! create_random_proof, //! prepare_verifying_key, @@ -145,16 +145,16 @@ //! In order to convince others you didn't, a multi-party //! computation (MPC) can be used. The MPC has the property that //! only one participant needs to be honest for the parameters to -//! be secure. This crate (`phase2`) is about creating parameters +//! be secure. This crate (`phase21`) is about creating parameters //! securely using such an MPC. //! -//! Let's start by using `phase2` to create some base parameters +//! Let's start by using `phase21` to create some base parameters //! for our circuit: //! //! ```rust,ignore -//! extern crate phase2; +//! extern crate phase21; //! -//! let mut params = phase2::MPCParameters::new(CubeRoot { +//! let mut params = phase21::MPCParameters::new(CubeRoot { //! cube_root: None //! }).unwrap(); //! ``` @@ -190,21 +190,21 @@ //! //! // We need to check the `contributions` to see if our `hash` //! // is in it (see above, when we first contributed) -//! assert!(phase2::contains_contribution(&contributions, &hash)); +//! assert!(phase21::contains_contribution(&contributions, &hash)); //! ``` //! //! Great, now if you're happy, grab the Groth16 `Parameters` with //! `params.params()`, so that you can interact with the bellman APIs //! just as before. -extern crate bellman; +extern crate bellperson; extern crate blake2b_simd; extern crate byteorder; extern crate crossbeam; +extern crate ff; extern crate num_cpus; -extern crate pairing; +extern crate paired; extern crate rand; -extern crate ff; use blake2b_simd::State as Blake2b; @@ -216,14 +216,14 @@ use std::{ sync::Arc, }; -use pairing::{ +use paired::{ bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, - CurveAffine, CurveProjective, EncodedPoint, Wnaf, Engine, + CurveAffine, CurveProjective, EncodedPoint, Engine, Wnaf, }; use ff::{Field, PrimeField}; -use bellman::{ +use bellperson::{ groth16::{Parameters, VerifyingKey}, multicore::Worker, Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable, @@ -532,55 +532,57 @@ impl MPCParameters { assert_eq!(a_g1.len(), ext.len()); // Evaluate polynomials in multiple threads - worker.scope(a_g1.len(), |scope, chunk| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 - .chunks_mut(chunk) - .zip(b_g1.chunks_mut(chunk)) - .zip(b_g2.chunks_mut(chunk)) - .zip(ext.chunks_mut(chunk)) - .zip(at.chunks(chunk)) - .zip(bt.chunks(chunk)) - .zip(ct.chunks(chunk)) - { - let coeffs_g1 = coeffs_g1.clone(); - let coeffs_g2 = coeffs_g2.clone(); - let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); - let beta_coeffs_g1 = beta_coeffs_g1.clone(); - - scope.spawn(move |_| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 - .iter_mut() - .zip(b_g1.iter_mut()) - .zip(b_g2.iter_mut()) - .zip(ext.iter_mut()) - .zip(at.iter()) - .zip(bt.iter()) - .zip(ct.iter()) - { - for &(coeff, lag) in at { - a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); + worker + .scope(a_g1.len(), |scope, chunk| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .chunks_mut(chunk) + .zip(b_g1.chunks_mut(chunk)) + .zip(b_g2.chunks_mut(chunk)) + .zip(ext.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(bt.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let coeffs_g1 = coeffs_g1.clone(); + let coeffs_g2 = coeffs_g2.clone(); + let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); + let beta_coeffs_g1 = beta_coeffs_g1.clone(); + + scope.spawn(move |_| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .iter_mut() + .zip(b_g1.iter_mut()) + .zip(b_g2.iter_mut()) + .zip(ext.iter_mut()) + .zip(at.iter()) + .zip(bt.iter()) + .zip(ct.iter()) + { + for &(coeff, lag) in at { + a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in bt { + b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); + ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in ct { + ext.add_assign(&coeffs_g1[lag].mul(coeff)); + } } - for &(coeff, lag) in bt { - b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); - ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); - } - - for &(coeff, lag) in ct { - ext.add_assign(&coeffs_g1[lag].mul(coeff)); - } - } - - // Batch normalize - G1::batch_normalization(a_g1); - G1::batch_normalization(b_g1); - G2::batch_normalization(b_g2); - G1::batch_normalization(ext); - }); - } - }).unwrap(); + // Batch normalize + G1::batch_normalization(a_g1); + G1::batch_normalization(b_g1); + G2::batch_normalization(b_g2); + G1::batch_normalization(ext); + }); + } + }) + .unwrap(); } let worker = Worker::new(); @@ -723,7 +725,8 @@ impl MPCParameters { } }); } - }).unwrap(); + }) + .unwrap(); // Perform batch normalization crossbeam::thread::scope(|scope| { @@ -732,7 +735,8 @@ impl MPCParameters { C::Projective::batch_normalization(projective); }); } - }).unwrap(); + }) + .unwrap(); // Turn it all back into affine points for (projective, affine) in projective.iter().zip(bases.iter_mut()) { @@ -1230,7 +1234,8 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { sx.lock().unwrap().add_assign(&local_sx); }); } - }).unwrap(); + }) + .unwrap(); let s = s.lock().unwrap().into_affine(); let sx = sx.lock().unwrap().into_affine(); From 47401e7450b61ef4a06706b7691657d085e2eb3d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 28 Nov 2019 15:34:21 +0100 Subject: [PATCH 20/40] feat: update to latest dependencies --- lib.rs | 144 +++++++++++++++++++++++++++------------------------------ 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/lib.rs b/lib.rs index 6bfe8ee63..2e4cd7e16 100644 --- a/lib.rs +++ b/lib.rs @@ -202,13 +202,11 @@ extern crate blake2b_simd; extern crate byteorder; extern crate crossbeam; extern crate ff; +extern crate groupy; extern crate num_cpus; extern crate paired; extern crate rand; - -use blake2b_simd::State as Blake2b; - -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +extern crate rand_chacha; use std::{ fs::File, @@ -216,20 +214,21 @@ use std::{ sync::Arc, }; -use paired::{ - bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, - CurveAffine, CurveProjective, EncodedPoint, Engine, Wnaf, -}; - -use ff::{Field, PrimeField}; - use bellperson::{ groth16::{Parameters, VerifyingKey}, multicore::Worker, Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable, }; - -use rand::{ChaChaRng, Rand, Rng, SeedableRng}; +use blake2b_simd::State as Blake2b; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use ff::{Field, PrimeField}; +use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; +use paired::{ + bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, + Engine, PairingCurveAffine, +}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaChaRng; /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. @@ -532,57 +531,55 @@ impl MPCParameters { assert_eq!(a_g1.len(), ext.len()); // Evaluate polynomials in multiple threads - worker - .scope(a_g1.len(), |scope, chunk| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 - .chunks_mut(chunk) - .zip(b_g1.chunks_mut(chunk)) - .zip(b_g2.chunks_mut(chunk)) - .zip(ext.chunks_mut(chunk)) - .zip(at.chunks(chunk)) - .zip(bt.chunks(chunk)) - .zip(ct.chunks(chunk)) - { - let coeffs_g1 = coeffs_g1.clone(); - let coeffs_g2 = coeffs_g2.clone(); - let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); - let beta_coeffs_g1 = beta_coeffs_g1.clone(); - - scope.spawn(move |_| { - for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 - .iter_mut() - .zip(b_g1.iter_mut()) - .zip(b_g2.iter_mut()) - .zip(ext.iter_mut()) - .zip(at.iter()) - .zip(bt.iter()) - .zip(ct.iter()) - { - for &(coeff, lag) in at { - a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); - } - - for &(coeff, lag) in bt { - b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); - b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); - ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); - } - - for &(coeff, lag) in ct { - ext.add_assign(&coeffs_g1[lag].mul(coeff)); - } + worker.scope(a_g1.len(), |scope, chunk| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .chunks_mut(chunk) + .zip(b_g1.chunks_mut(chunk)) + .zip(b_g2.chunks_mut(chunk)) + .zip(ext.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(bt.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let coeffs_g1 = coeffs_g1.clone(); + let coeffs_g2 = coeffs_g2.clone(); + let alpha_coeffs_g1 = alpha_coeffs_g1.clone(); + let beta_coeffs_g1 = beta_coeffs_g1.clone(); + + scope.spawn(move |_| { + for ((((((a_g1, b_g1), b_g2), ext), at), bt), ct) in a_g1 + .iter_mut() + .zip(b_g1.iter_mut()) + .zip(b_g2.iter_mut()) + .zip(ext.iter_mut()) + .zip(at.iter()) + .zip(bt.iter()) + .zip(ct.iter()) + { + for &(coeff, lag) in at { + a_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + ext.add_assign(&beta_coeffs_g1[lag].mul(coeff)); } - // Batch normalize - G1::batch_normalization(a_g1); - G1::batch_normalization(b_g1); - G2::batch_normalization(b_g2); - G1::batch_normalization(ext); - }); - } - }) - .unwrap(); + for &(coeff, lag) in bt { + b_g1.add_assign(&coeffs_g1[lag].mul(coeff)); + b_g2.add_assign(&coeffs_g2[lag].mul(coeff)); + ext.add_assign(&alpha_coeffs_g1[lag].mul(coeff)); + } + + for &(coeff, lag) in ct { + ext.add_assign(&coeffs_g1[lag].mul(coeff)); + } + } + + // Batch normalize + G1::batch_normalization(a_g1); + G1::batch_normalization(b_g1); + G2::batch_normalization(b_g2); + G1::batch_normalization(ext); + }); + } + }); } let worker = Worker::new(); @@ -1178,7 +1175,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res } /// Checks if pairs have the same ratio. -fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> bool { +fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> bool { g1.0.pairing_with(&g2.1) == g1.1.pairing_with(&g2.0) } @@ -1221,7 +1218,7 @@ fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { let mut local_sx = G::Projective::zero(); for (v1, v2) in v1.iter().zip(v2.iter()) { - let rho = G::Scalar::rand(rng); + let rho = G::Scalar::random(rng); let mut wnaf = wnaf.scalar(rho.into_repr()); let v1 = wnaf.base(v1.into_projective()); let v2 = wnaf.base(v2.into_projective()); @@ -1254,10 +1251,10 @@ struct PrivateKey { /// in different parameters. fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateKey) { // Sample random delta - let delta: Fr = rng.gen(); + let delta: Fr = Fr::random(rng); // Compute delta s-pair in G1 - let s = G1::rand(rng).into_affine(); + let s = G1::random(rng).into_affine(); let s_delta = s.mul(delta).into_affine(); // H(cs_hash | | s | s_delta) @@ -1299,20 +1296,13 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK /// Hashes to G2 using the first 32 bytes of `digest`. Panics if `digest` is less /// than 32 bytes. -fn hash_to_g2(mut digest: &[u8]) -> G2 { +fn hash_to_g2(digest: &[u8]) -> G2 { assert!(digest.len() >= 32); - let mut seed = Vec::with_capacity(8); - - for _ in 0..8 { - seed.push( - digest - .read_u32::() - .expect("assertion above guarantees this to work"), - ); - } + let mut seed = [0u8; 32]; + seed.copy_from_slice(&digest[..32]); - ChaChaRng::from_seed(&seed).gen() + G2::random(&mut ChaChaRng::from_seed(seed)) } /// Abstraction over a writer which hashes the data being written. From 0fb38353ae739c74b2047a0e7dbc4d68dd788f6a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 28 Nov 2019 15:35:22 +0100 Subject: [PATCH 21/40] refactor: upgrade to edition 2018 --- lib.rs | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/lib.rs b/lib.rs index 2e4cd7e16..d0cec8453 100644 --- a/lib.rs +++ b/lib.rs @@ -11,10 +11,6 @@ //! a field element. //! //! ```rust -//! extern crate paired; -//! extern crate bellperson; -//! extern crate ff; -//! //! use paired::Engine; //! use ff::Field; //! use bellperson::{ @@ -85,8 +81,6 @@ //! let's create some parameters and make some proofs. //! //! ```rust,ignore -//! extern crate rand; -//! //! use paired::bls12_381::{Bls12, Fr}; //! use bellperson::groth16::{ //! generate_random_parameters, @@ -152,8 +146,6 @@ //! for our circuit: //! //! ```rust,ignore -//! extern crate phase21; -//! //! let mut params = phase21::MPCParameters::new(CubeRoot { //! cube_root: None //! }).unwrap(); @@ -197,17 +189,6 @@ //! `params.params()`, so that you can interact with the bellman APIs //! just as before. -extern crate bellperson; -extern crate blake2b_simd; -extern crate byteorder; -extern crate crossbeam; -extern crate ff; -extern crate groupy; -extern crate num_cpus; -extern crate paired; -extern crate rand; -extern crate rand_chacha; - use std::{ fs::File, io::{self, BufReader, Read, Write}, @@ -1194,7 +1175,7 @@ fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> /// ... with high probability. fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { use rand::thread_rng; - use std::sync::{Arc, Mutex}; + use std::sync::Mutex; assert_eq!(v1.len(), v2.len()); @@ -1330,7 +1311,7 @@ impl HashWriter { } /// Destroy this writer and return the hash of what was written. - pub fn into_hash(mut self) -> [u8; 64] { + pub fn into_hash(self) -> [u8; 64] { let mut tmp = [0u8; 64]; tmp.copy_from_slice(self.hasher.finalize().as_ref()); tmp From ec1a344ce2f553b5492432cb5923123ca7fc72ee Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 28 Nov 2019 15:39:46 +0100 Subject: [PATCH 22/40] refactor: happy clippy --- lib.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/lib.rs b/lib.rs index d0cec8453..bee186ec7 100644 --- a/lib.rs +++ b/lib.rs @@ -188,6 +188,7 @@ //! Great, now if you're happy, grab the Groth16 `Parameters` with //! `params.params()`, so that you can interact with the bellman APIs //! just as before. +#![deny(clippy::all, clippy::perf, clippy::correctness)] use std::{ fs::File, @@ -339,7 +340,7 @@ pub struct MPCParameters { impl PartialEq for MPCParameters { fn eq(&self, other: &MPCParameters) -> bool { self.params == other.params - && &self.cs_hash[..] == &other.cs_hash[..] + && self.cs_hash[..] == other.cs_hash[..] && self.contributions == other.contributions } } @@ -482,6 +483,7 @@ impl MPCParameters { let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; + #[allow(clippy::too_many_arguments)] fn eval( // Lagrange coefficients for tau coeffs_g1: Arc>, @@ -607,8 +609,8 @@ impl MPCParameters { let vk = VerifyingKey { alpha_g1: alpha, - beta_g1: beta_g1, - beta_g2: beta_g2, + beta_g1, + beta_g2, gamma_g2: G2Affine::one(), delta_g1: G1Affine::one(), delta_g2: G2Affine::one(), @@ -616,7 +618,7 @@ impl MPCParameters { }; let params = Parameters { - vk: vk, + vk, h: Arc::new(h), l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), @@ -654,8 +656,8 @@ impl MPCParameters { cs_hash.copy_from_slice(h.as_ref()); Ok(MPCParameters { - params: params, - cs_hash: cs_hash, + params, + cs_hash, contributions: vec![], }) } @@ -794,7 +796,7 @@ impl MPCParameters { } // cs_hash should be the same - if &initial_params.cs_hash[..] != &self.cs_hash[..] { + if initial_params.cs_hash[..] != self.cs_hash[..] { return Err(()); } @@ -1021,7 +1023,7 @@ impl PartialEq for PublicKey { && self.s == other.s && self.s_delta == other.s_delta && self.r_delta == other.r_delta - && &self.transcript[..] == &other.transcript[..] + && self.transcript[..] == other.transcript[..] } } @@ -1034,7 +1036,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res } // None of the previous transformations should change - if &before.contributions[..] != &after.contributions[0..before.contributions.len()] { + if before.contributions[..] != after.contributions[0..before.contributions.len()] { return Err(()); } @@ -1077,7 +1079,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res } // cs_hash should be the same - if &before.cs_hash[..] != &after.cs_hash[..] { + if before.cs_hash[..] != after.cs_hash[..] { return Err(()); } @@ -1266,12 +1268,12 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK ( PublicKey { delta_after: current.params.vk.delta_g1.mul(delta).into_affine(), - s: s, - s_delta: s_delta, - r_delta: r_delta, - transcript: transcript, + s, + s_delta, + r_delta, + transcript, }, - PrivateKey { delta: delta }, + PrivateKey { delta }, ) } @@ -1305,7 +1307,7 @@ impl HashWriter { /// Construct a new `HashWriter` given an existing `writer` by value. pub fn new(writer: W) -> Self { HashWriter { - writer: writer, + writer, hasher: Blake2b::new(), } } @@ -1339,10 +1341,10 @@ impl Write for HashWriter { /// and so doesn't implement `PartialEq` for `[T; 64]` pub fn contains_contribution(contributions: &[[u8; 64]], my_contribution: &[u8; 64]) -> bool { for contrib in contributions { - if &contrib[..] == &my_contribution[..] { + if contrib[..] == my_contribution[..] { return true; } } - return false; + false } From e66caf832b28e612e2f8c783f174598e8e177d94 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 18 Dec 2019 21:08:05 +0100 Subject: [PATCH 23/40] cleanups and use a little rayon --- lib.rs | 60 ++++++++++++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/lib.rs b/lib.rs index bee186ec7..c72e168a7 100644 --- a/lib.rs +++ b/lib.rs @@ -211,6 +211,7 @@ use paired::{ }; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; +use rayon::prelude::*; /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. @@ -389,8 +390,8 @@ impl MPCParameters { m *= 2; exp += 1; - // Powers of Tau ceremony can't support more than 2^21 - if exp > 21 { + // Powers of Tau ceremony can't support more than 2^30 + if exp > 30 { return Err(SynthesisError::PolynomialDegreeTooLarge); } } @@ -614,36 +615,36 @@ impl MPCParameters { gamma_g2: G2Affine::one(), delta_g1: G1Affine::one(), delta_g2: G2Affine::one(), - ic: ic.into_iter().map(|e| e.into_affine()).collect(), + ic: ic.into_par_iter().map(|e| e.into_affine()).collect(), }; let params = Parameters { vk, h: Arc::new(h), - l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + l: Arc::new(l.into_par_iter().map(|e| e.into_affine()).collect()), // Filter points at infinity away from A/B queries a: Arc::new( - a_g1.into_iter() + a_g1.into_par_iter() .filter(|e| !e.is_zero()) .map(|e| e.into_affine()) .collect(), ), b_g1: Arc::new( - b_g1.into_iter() + b_g1.into_par_iter() .filter(|e| !e.is_zero()) .map(|e| e.into_affine()) .collect(), ), b_g2: Arc::new( - b_g2.into_iter() + b_g2.into_par_iter() .filter(|e| !e.is_zero()) .map(|e| e.into_affine()) .collect(), ), }; - let h = { + let cs_hash = { let sink = io::sink(); let mut sink = HashWriter::new(sink); @@ -652,9 +653,6 @@ impl MPCParameters { sink.into_hash() }; - let mut cs_hash = [0; 64]; - cs_hash.copy_from_slice(h.as_ref()); - Ok(MPCParameters { params, cs_hash, @@ -709,19 +707,17 @@ impl MPCParameters { .unwrap(); // Perform batch normalization - crossbeam::thread::scope(|scope| { - for projective in projective.chunks_mut(chunk_size) { - scope.spawn(move |_| { - C::Projective::batch_normalization(projective); - }); - } - }) - .unwrap(); + projective + .par_chunks_mut(chunk_size) + .for_each(|p| C::Projective::batch_normalization(p)); // Turn it all back into affine points - for (projective, affine) in projective.iter().zip(bases.iter_mut()) { - *affine = projective.into_affine(); - } + projective + .par_iter() + .zip(bases.par_iter_mut()) + .for_each(|(projective, affine)| { + *affine = projective.into_affine(); + }); } let delta_inv = privkey.delta.inverse().expect("nonzero"); @@ -742,10 +738,7 @@ impl MPCParameters { let sink = io::sink(); let mut sink = HashWriter::new(sink); pubkey.write(&mut sink).unwrap(); - let h = sink.into_hash(); - let mut response = [0u8; 64]; - response.copy_from_slice(h.as_ref()); - response + sink.into_hash() } } @@ -843,10 +836,7 @@ impl MPCParameters { let sink = io::sink(); let mut sink = HashWriter::new(sink); pubkey.write(&mut sink).unwrap(); - let h = sink.into_hash(); - let mut response = [0u8; 64]; - response.copy_from_slice(h.as_ref()); - result.push(response); + result.push(sink.into_hash()); } } @@ -1150,11 +1140,8 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res let sink = io::sink(); let mut sink = HashWriter::new(sink); pubkey.write(&mut sink).unwrap(); - let h = sink.into_hash(); - let mut response = [0u8; 64]; - response.copy_from_slice(h.as_ref()); - Ok(response) + Ok(sink.into_hash()) } /// Checks if pairs have the same ratio. @@ -1258,11 +1245,10 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK // This avoids making a weird assumption about the hash into the // group. - let mut transcript = [0; 64]; - transcript.copy_from_slice(h.as_ref()); + let transcript = h; // Compute delta s-pair in G2 - let r = hash_to_g2(h.as_ref()).into_affine(); + let r = hash_to_g2(&h).into_affine(); let r_delta = r.mul(delta).into_affine(); ( From f5f3bba04bd96ab72f42215c754998c314d191cb Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 5 Mar 2020 12:55:21 +0100 Subject: [PATCH 24/40] feat: updated dependencies, add logs and reduce memory consumption --- lib.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/lib.rs b/lib.rs index c72e168a7..6d1a6c7c1 100644 --- a/lib.rs +++ b/lib.rs @@ -205,6 +205,7 @@ use blake2b_simd::State as Blake2b; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use ff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; +use log::{error, info}; use paired::{ bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, Engine, PairingCurveAffine, @@ -227,6 +228,43 @@ struct KeypairAssembly { ct_aux: Vec>, } +impl KeypairAssembly { + /// Returns the size (stack plus heap) of the `KeypairAssembly` in bytes. + fn size(&self) -> usize { + use std::mem::{size_of, size_of_val}; + + let mut size = 3 * size_of::(); + size += 6 * size_of::>>(); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.at_inputs); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.bt_inputs); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.ct_inputs); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.at_aux); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.bt_aux); + size += size_of_val::<[Vec<(E::Fr, usize)>]>(&self.ct_aux); + + for el in self.at_inputs.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + for el in self.bt_inputs.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + for el in self.ct_inputs.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + for el in self.at_aux.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + for el in self.bt_aux.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + for el in self.ct_aux.iter() { + size += size_of_val::<[(E::Fr, usize)]>(el); + } + + size + } +} + impl ConstraintSystem for KeypairAssembly { type Root = Self; @@ -383,6 +421,14 @@ impl MPCParameters { ); } + info!( + "phase2::MPCParameters::new() Constraint System: n_constraints={}, n_inputs={}, n_aux={}, size={}b", + assembly.num_constraints, + assembly.num_inputs, + assembly.num_aux, + assembly.size() + ); + // Compute the size of our evaluation domain let mut m = 1; let mut exp = 0; @@ -397,6 +443,7 @@ impl MPCParameters { } // Try to load "phase1radix2m{}" + info!("phase2::MPCParameters::new() phase1.5_file=phase1radix2m{}", exp); let f = match File::open(format!("phase1radix2m{}", exp)) { Ok(f) => f, Err(e) => { @@ -445,21 +492,25 @@ impl MPCParameters { let beta_g1 = read_g1(f)?; let beta_g2 = read_g2(f)?; + info!("phase2::MPCParameters::new() reading coeffs_g1 from phase1.5 file"); let mut coeffs_g1 = Vec::with_capacity(m); for _ in 0..m { coeffs_g1.push(read_g1(f)?); } + info!("phase2::MPCParameters::new() reading coeffs_g2 from phase1.5 file"); let mut coeffs_g2 = Vec::with_capacity(m); for _ in 0..m { coeffs_g2.push(read_g2(f)?); } + info!("phase2::MPCParameters::new() reading alpha_coeffs_g1 from phase1.5 file"); let mut alpha_coeffs_g1 = Vec::with_capacity(m); for _ in 0..m { alpha_coeffs_g1.push(read_g1(f)?); } + info!("phase2::MPCParameters::new() reading beta_coeffs_g1 from phase1.5 file"); let mut beta_coeffs_g1 = Vec::with_capacity(m); for _ in 0..m { beta_coeffs_g1.push(read_g1(f)?); @@ -473,16 +524,16 @@ impl MPCParameters { let alpha_coeffs_g1 = Arc::new(alpha_coeffs_g1); let beta_coeffs_g1 = Arc::new(beta_coeffs_g1); - let mut h = Vec::with_capacity(m - 1); - for _ in 0..(m - 1) { - h.push(read_g1(f)?); - } - let mut ic = vec![G1::zero(); assembly.num_inputs]; + info!("phase2::MPCParameters::new() initialized ic vector"); let mut l = vec![G1::zero(); assembly.num_aux]; + info!("phase2::MPCParameters::new() initialized l vector"); let mut a_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + info!("phase2::MPCParameters::new() initialized a_g1 vector"); let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + info!("phase2::MPCParameters::new() initialized b_g1 vector"); let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; + info!("phase2::MPCParameters::new() initialized b_g2 vector"); #[allow(clippy::too_many_arguments)] fn eval( @@ -569,6 +620,7 @@ impl MPCParameters { let worker = Worker::new(); // Evaluate for inputs. + info!("phase2::MPCParameters::new() evaluating polynomials for inputs"); eval( coeffs_g1.clone(), coeffs_g2.clone(), @@ -585,6 +637,7 @@ impl MPCParameters { ); // Evaluate for auxillary variables. + info!("phase2::MPCParameters::new() evaluating polynomials for auxillary variables"); eval( coeffs_g1.clone(), coeffs_g2.clone(), @@ -618,6 +671,18 @@ impl MPCParameters { ic: ic.into_par_iter().map(|e| e.into_affine()).collect(), }; + // Reclaim the memory used by these vectors prior to reading in `h`. + drop(coeffs_g1); + drop(coeffs_g2); + drop(alpha_coeffs_g1); + drop(beta_coeffs_g1); + + info!("phase2::MPCParameters::new() reading h from phase1.5 file"); + let mut h = Vec::with_capacity(m - 1); + for _ in 0..(m - 1) { + h.push(read_g1(f)?); + } + let params = Parameters { vk, h: Arc::new(h), @@ -721,10 +786,15 @@ impl MPCParameters { } let delta_inv = privkey.delta.inverse().expect("nonzero"); + info!("phase2::MPCParameters::contribute() cloning l"); let mut l = (&self.params.l[..]).to_vec(); + info!("phase2::MPCParameters::contribute() cloning h"); let mut h = (&self.params.h[..]).to_vec(); + info!("phase2::MPCParameters::contribute() performing batch exponentiation of l"); batch_exp(&mut l, delta_inv); + info!("phase2::MPCParameters::contribute() performing batch exponentiation of h"); batch_exp(&mut h, delta_inv); + info!("phase2::MPCParameters::contribute() finished batch exponentiations"); self.params.l = Arc::new(l); self.params.h = Arc::new(h); @@ -752,44 +822,55 @@ impl MPCParameters { // H/L will change, but should have same length if initial_params.params.h.len() != self.params.h.len() { + error!("phase2::MPCParameters::verify() h's length has changed"); return Err(()); } if initial_params.params.l.len() != self.params.l.len() { + error!("phase2::MPCParameters::verify() l's length has changed"); return Err(()); } // A/B_G1/B_G2 doesn't change at all if initial_params.params.a != self.params.a { + error!("phase2::MPCParameters::verify() evaluated QAP a polynomial has changed"); return Err(()); } if initial_params.params.b_g1 != self.params.b_g1 { + error!("phase2::MPCParameters::verify() evaluated QAP b_g1 polynomial has changed"); return Err(()); } if initial_params.params.b_g2 != self.params.b_g2 { + error!("phase2::MPCParameters::verify() evaluated QAP b_g2 polynomial has changed"); return Err(()); } // alpha/beta/gamma don't change if initial_params.params.vk.alpha_g1 != self.params.vk.alpha_g1 { + error!("phase2::MPCParameters::verify() vk's alpha has changed"); return Err(()); } if initial_params.params.vk.beta_g1 != self.params.vk.beta_g1 { + error!("phase2::MPCParameters::verify() vk's beta_g1 has changed"); return Err(()); } if initial_params.params.vk.beta_g2 != self.params.vk.beta_g2 { + error!("phase2::MPCParameters::verify() vk's beta_g2 has changed"); return Err(()); } if initial_params.params.vk.gamma_g2 != self.params.vk.gamma_g2 { + error!("phase2::MPCParameters::verify() vk's gamma has changed"); return Err(()); } // IC shouldn't change, as gamma doesn't change if initial_params.params.vk.ic != self.params.vk.ic { + error!("phase2::MPCParameters::verify() vk's ic has changed"); return Err(()); } // cs_hash should be the same if initial_params.cs_hash[..] != self.cs_hash[..] { + error!("phase2::MPCParameters::verify() cs_hash has changed"); return Err(()); } @@ -815,6 +896,7 @@ impl MPCParameters { // The transcript must be consistent if &pubkey.transcript[..] != h.as_ref() { + error!("phase2::MPCParameters::verify() transcripts differ"); return Err(()); } @@ -822,11 +904,13 @@ impl MPCParameters { // Check the signature of knowledge if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { + error!("phase2::MPCParameters::verify() pubkey's r and s were shifted by different deltas"); return Err(()); } // Check the change from the old delta is consistent if !same_ratio((current_delta, pubkey.delta_after), (r, pubkey.r_delta)) { + error!("phase2::MPCParameters::verify() contribution's delta and r where shifted differently"); return Err(()); } @@ -842,6 +926,7 @@ impl MPCParameters { // Current parameters should have consistent delta in G1 if current_delta != self.params.vk.delta_g1 { + error!("phase2::MPCParameters::verify() vk's delta_g1 differs from calculated delta"); return Err(()); } @@ -850,6 +935,7 @@ impl MPCParameters { (G1Affine::one(), current_delta), (G2Affine::one(), self.params.vk.delta_g2), ) { + error!("phase2::MPCParameters::verify() shift in vk's delta_g2 is inconsistent with calculated delta"); return Err(()); } @@ -858,6 +944,7 @@ impl MPCParameters { merge_pairs(&initial_params.params.h, &self.params.h), (self.params.vk.delta_g2, G2Affine::one()), // reversed for inverse ) { + error!("phase2::MPCParameters::verify() h queries have not shifted by delta^-1"); return Err(()); } @@ -865,6 +952,7 @@ impl MPCParameters { merge_pairs(&initial_params.params.l, &self.params.l), (self.params.vk.delta_g2, G2Affine::one()), // reversed for inverse ) { + error!("phase2::MPCParameters::verify() l queries have not shifted by delta^-1"); return Err(()); } @@ -1022,54 +1110,73 @@ impl PartialEq for PublicKey { pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Result<[u8; 64], ()> { // Transformation involves a single new object if after.contributions.len() != (before.contributions.len() + 1) { + error!( + "phase2::verify_contribution() 'after' params do not contain exactly one more \ + contribution than the 'before' params: n_contributions_before={}, \ + n_contributions_after={}", + before.contributions.len(), + after.contributions.len() + ); return Err(()); } // None of the previous transformations should change if before.contributions[..] != after.contributions[0..before.contributions.len()] { + error!("phase2::verify_contribution() 'after' params contributions differ from 'before' params contributions"); return Err(()); } // H/L will change, but should have same length if before.params.h.len() != after.params.h.len() { + error!("phase2::verify_contribution() length of h has changed"); return Err(()); } if before.params.l.len() != after.params.l.len() { + error!("phase2::verify_contribution() length of l has changed"); return Err(()); } // A/B_G1/B_G2 doesn't change at all if before.params.a != after.params.a { + error!("phase2::verify_contribution() evaluated QAP a polynomial has changed"); return Err(()); } if before.params.b_g1 != after.params.b_g1 { + error!("phase2::verify_contribution() evaluated QAP b_g1 polynomial has changed"); return Err(()); } if before.params.b_g2 != after.params.b_g2 { + error!("phase2::verify_contribution() evaluated QAP b_g2 polynomial has changed"); return Err(()); } // alpha/beta/gamma don't change if before.params.vk.alpha_g1 != after.params.vk.alpha_g1 { + error!("phase2::verify_contribution() vk's alpha_g1 hash changed"); return Err(()); } if before.params.vk.beta_g1 != after.params.vk.beta_g1 { + error!("phase2::verify_contribution() vk's beta_g1 has changed"); return Err(()); } if before.params.vk.beta_g2 != after.params.vk.beta_g2 { + error!("phase2::verify_contribution() vk's beta_g2 changed"); return Err(()); } if before.params.vk.gamma_g2 != after.params.vk.gamma_g2 { + error!("phase2::verify_contribution() vk's gamma_g2 has changed"); return Err(()); } // IC shouldn't change, as gamma doesn't change if before.params.vk.ic != after.params.vk.ic { + error!("phase2::verify_contribution() vk's ic has changed"); return Err(()); } // cs_hash should be the same if before.cs_hash[..] != after.cs_hash[..] { + error!("phase2::verify_contribution() cs_hash has changed"); return Err(()); } @@ -1091,6 +1198,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res // The transcript must be consistent if &pubkey.transcript[..] != h.as_ref() { + error!("phase2::verify_contribution() inconsistent transcript"); return Err(()); } @@ -1098,6 +1206,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res // Check the signature of knowledge if !same_ratio((r, pubkey.r_delta), (pubkey.s, pubkey.s_delta)) { + error!("phase2::verify_contribution() contribution's r and s were shifted with different deltas"); return Err(()); } @@ -1106,11 +1215,13 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res (before.params.vk.delta_g1, pubkey.delta_after), (r, pubkey.r_delta), ) { + error!("phase2::verify_contribution() contribution's delta and r where shifted with different delta"); return Err(()); } // Current parameters should have consistent delta in G1 if pubkey.delta_after != after.params.vk.delta_g1 { + error!("phase2::verify_contribution() contribution's delta in G1 differs from vk's delta_g1"); return Err(()); } @@ -1119,6 +1230,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res (G1Affine::one(), pubkey.delta_after), (G2Affine::one(), after.params.vk.delta_g2), ) { + error!("phase2::verify_contribution() contribution's shift in delta (G1) is inconsistent with vk's shift in delta (G2)"); return Err(()); } @@ -1127,6 +1239,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res merge_pairs(&before.params.h, &after.params.h), (after.params.vk.delta_g2, before.params.vk.delta_g2), // reversed for inverse ) { + error!("phase2::verify_contribution() h was not updated by delta^-1"); return Err(()); } @@ -1134,6 +1247,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res merge_pairs(&before.params.l, &after.params.l), (after.params.vk.delta_g2, before.params.vk.delta_g2), // reversed for inverse ) { + error!("phase2::verify_contribution() l was not updated by delta^-1"); return Err(()); } From 46a26356e493f954ec641c38dcfced9ae9d400e4 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 13 May 2020 00:37:54 +0200 Subject: [PATCH 25/40] use new lc interface --- lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib.rs b/lib.rs index 6d1a6c7c1..a8eaff3c0 100644 --- a/lib.rs +++ b/lib.rs @@ -320,7 +320,7 @@ impl ConstraintSystem for KeypairAssembly { aux: &mut [Vec<(E::Fr, usize)>], this_constraint: usize, ) { - for &(var, coeff) in l.as_ref() { + for (&var, &coeff) in l.iter() { match var.get_unchecked() { Index::Input(id) => inputs[id].push((coeff, this_constraint)), Index::Aux(id) => aux[id].push((coeff, this_constraint)), @@ -443,7 +443,10 @@ impl MPCParameters { } // Try to load "phase1radix2m{}" - info!("phase2::MPCParameters::new() phase1.5_file=phase1radix2m{}", exp); + info!( + "phase2::MPCParameters::new() phase1.5_file=phase1radix2m{}", + exp + ); let f = match File::open(format!("phase1radix2m{}", exp)) { Ok(f) => f, Err(e) => { @@ -1221,7 +1224,9 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res // Current parameters should have consistent delta in G1 if pubkey.delta_after != after.params.vk.delta_g1 { - error!("phase2::verify_contribution() contribution's delta in G1 differs from vk's delta_g1"); + error!( + "phase2::verify_contribution() contribution's delta in G1 differs from vk's delta_g1" + ); return Err(()); } From 905dcabfbbea4fd4197afec2e495d802e3e6c693 Mon Sep 17 00:00:00 2001 From: DrPeterVanNostrand Date: Tue, 16 Jun 2020 17:05:25 -0400 Subject: [PATCH 26/40] reduce param size during contribution stage --- lib.rs | 183 ++++++++++++++++++-- small.rs | 502 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 675 insertions(+), 10 deletions(-) create mode 100644 small.rs diff --git a/lib.rs b/lib.rs index a8eaff3c0..cb94753e7 100644 --- a/lib.rs +++ b/lib.rs @@ -190,7 +190,10 @@ //! just as before. #![deny(clippy::all, clippy::perf, clippy::correctness)] +pub mod small; + use std::{ + fmt::{self, Debug, Formatter}, fs::File, io::{self, BufReader, Read, Write}, sync::Arc, @@ -214,6 +217,8 @@ use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use rayon::prelude::*; +use crate::small::MPCSmall; + /// This is our assembly structure that we'll use to synthesize the /// circuit into a QAP. struct KeypairAssembly { @@ -376,6 +381,17 @@ pub struct MPCParameters { contributions: Vec, } +// Required by `assert_eq!()`. +impl Debug for MPCParameters { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("MPCParameters") + .field("params", &"") + .field("cs_hash", &self.cs_hash.to_vec()) + .field("contributions", &self.contributions.to_vec()) + .finish() + } +} + impl PartialEq for MPCParameters { fn eq(&self, other: &MPCParameters) -> bool { self.params == other.params @@ -422,14 +438,14 @@ impl MPCParameters { } info!( - "phase2::MPCParameters::new() Constraint System: n_constraints={}, n_inputs={}, n_aux={}, size={}b", + "phase2::MPCParameters::new() Constraint System: n_constraints={}, n_inputs={}, n_aux={}, memsize={}b", assembly.num_constraints, assembly.num_inputs, assembly.num_aux, assembly.size() ); - // Compute the size of our evaluation domain + // Compute the size of our evaluation domain, `m = 2^exp`. let mut m = 1; let mut exp = 0; while m < assembly.num_constraints { @@ -712,6 +728,16 @@ impl MPCParameters { ), }; + info!( + "phase2::MPCParameters::new() vector lengths: ic={}, h={}, l={}, a={}, b_g1={}, b_g2={}", + params.vk.ic.len(), + params.h.len(), + params.l.len(), + params.a.len(), + params.b_g1.len(), + params.b_g2.len() + ); + let cs_hash = { let sink = io::sink(); let mut sink = HashWriter::new(sink); @@ -733,6 +759,10 @@ impl MPCParameters { &self.params } + pub fn n_contributions(&self) -> usize { + self.contributions.len() + } + /// Contributes some randomness to the parameters. Only one /// contributor needs to be honest for the parameters to be /// secure. @@ -789,9 +819,9 @@ impl MPCParameters { } let delta_inv = privkey.delta.inverse().expect("nonzero"); - info!("phase2::MPCParameters::contribute() cloning l"); + info!("phase2::MPCParameters::contribute() copying l"); let mut l = (&self.params.l[..]).to_vec(); - info!("phase2::MPCParameters::contribute() cloning h"); + info!("phase2::MPCParameters::contribute() copying h"); let mut h = (&self.params.h[..]).to_vec(); info!("phase2::MPCParameters::contribute() performing batch exponentiation of l"); batch_exp(&mut l, delta_inv); @@ -976,6 +1006,31 @@ impl MPCParameters { Ok(()) } + /// Serializes these parameters as `MPCSmall`. + pub fn write_small(&self, mut writer: W) -> io::Result<()> { + writer.write_all(self.params.vk.delta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.params.vk.delta_g2.into_uncompressed().as_ref())?; + + writer.write_u32::(self.params.h.len() as u32)?; + for h in &*self.params.h { + writer.write_all(h.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.params.l.len() as u32)?; + for l in &*self.params.l { + writer.write_all(l.into_uncompressed().as_ref())?; + } + + writer.write_all(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + + Ok(()) + } + /// Deserialize these parameters. If `checked` is false, /// we won't perform curve validity and group order /// checks. @@ -992,12 +1047,109 @@ impl MPCParameters { contributions.push(PublicKey::read(&mut reader)?); } + info!( + "phase2::MPCParameters::read() vector lengths: ic={}, h={}, l={}, a={}, b_g1={}, \ + b_g2={}, contributions={}", + params.vk.ic.len(), + params.h.len(), + params.l.len(), + params.a.len(), + params.b_g1.len(), + params.b_g2.len(), + contributions.len(), + ); + Ok(MPCParameters { params, cs_hash, contributions, }) } + + // memcpy's the potentially large vectors behind Arc's (duplicates the arrays on the stack, + // does not increment ref-counts in `self`). + pub fn copy(&self) -> Self { + let mut params = self.clone(); + params.params.h = Arc::new((*self.params.h).clone()); + params.params.l = Arc::new((*self.params.l).clone()); + params.params.a = Arc::new((*self.params.a).clone()); + params.params.b_g1 = Arc::new((*self.params.b_g1).clone()); + params.params.b_g2 = Arc::new((*self.params.b_g2).clone()); + params + } + + // memcpy's the potentially large h and l vectors behind Arc's into a new `MPCSmall` (duplicates + // the h and l arrays on the stack, does not increment ref-counts for the h and l Arc's in `self`). + pub fn copy_small(&self) -> MPCSmall { + MPCSmall { + delta_g1: self.params.vk.delta_g1, + delta_g2: self.params.vk.delta_g2, + h: (*self.params.h).clone(), + l: (*self.params.l).clone(), + cs_hash: self.cs_hash, + contributions: self.contributions.clone(), + } + } + + // Updates `self` with a contribution (or contributions) that is in the `MPCSmall` params form. + // `MPCSmall` must contain at least one new contribution. This decrements the strong ref-counts + // by one for any Arc clones that were made from `self.h` and `self.l`. If either of `self`'s h + // and l Arc's have ref-count 1, then they will be dropped. + pub fn add_contrib(&mut self, contrib: MPCSmall) { + assert_eq!( + self.cs_hash[..], + contrib.cs_hash[..], + "large and small params have different cs_hash" + ); + + assert_eq!( + self.params.h.len(), + contrib.h.len(), + "large and small params have different h length" + ); + assert_eq!( + self.params.l.len(), + contrib.l.len(), + "large and small params have different l length" + ); + + assert!( + self.contributions.len() < contrib.contributions.len(), + "small params do not contain additional contributions" + ); + assert_eq!( + &self.contributions[..], + &contrib.contributions[..self.contributions.len()], + "small params cannot change prior contributions in large params" + ); + + // Unwrapping here is safe because we have already asserted that `contrib` contains at least + // one (new) contribution. + assert_eq!( + contrib.delta_g1, + contrib.contributions.last().unwrap().delta_after, + "small params are internally inconsistent wrt. G1 deltas" + ); + + let MPCSmall { delta_g1, delta_g2, h, l, contributions, .. } = contrib; + self.params.vk.delta_g1 = delta_g1; + self.params.vk.delta_g2 = delta_g2; + self.params.h = Arc::new(h); + self.params.l = Arc::new(l); + self.contributions = contributions; + } + + // Returns true if a pair of large and small MPC params contain equal values. It is not required + // that `self`'s h and l Arc's point to the same memory locations as `small`'s non-Arc h and l + // vectors. + pub fn has_last_contrib(&self, small: &MPCSmall) -> bool { + self.params.vk.delta_g1 == small.delta_g1 + && self.params.vk.delta_g2 == small.delta_g2 + && *self.params.h == small.h + && *self.params.l == small.l + && self.cs_hash[..] == small.cs_hash[..] + && self.contributions == small.contributions + } } /// This allows others to verify that you contributed. The hash produced @@ -1022,6 +1174,19 @@ struct PublicKey { transcript: [u8; 64], } +// Required by `assert_eq!()`. +impl Debug for PublicKey { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("PublicKey") + .field("delta_after", &self.delta_after) + .field("s", &self.s) + .field("s_delta", &self.s_delta) + .field("r_delta", &self.r_delta) + .field("transcript", &self.transcript.to_vec()) + .finish() + } +} + impl PublicKey { fn write(&self, mut writer: W) -> io::Result<()> { writer.write_all(self.delta_after.into_uncompressed().as_ref())?; @@ -1111,7 +1276,6 @@ impl PartialEq for PublicKey { /// Verify a contribution, given the old parameters and /// the new parameters. Returns the hash of the contribution. pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Result<[u8; 64], ()> { - // Transformation involves a single new object if after.contributions.len() != (before.contributions.len() + 1) { error!( "phase2::verify_contribution() 'after' params do not contain exactly one more \ @@ -1247,7 +1411,6 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res error!("phase2::verify_contribution() h was not updated by delta^-1"); return Err(()); } - if !same_ratio( merge_pairs(&before.params.l, &after.params.l), (after.params.vk.delta_g2, before.params.vk.delta_g2), // reversed for inverse @@ -1264,7 +1427,7 @@ pub fn verify_contribution(before: &MPCParameters, after: &MPCParameters) -> Res } /// Checks if pairs have the same ratio. -fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> bool { +pub(crate) fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> bool { g1.0.pairing_with(&g2.1) == g1.1.pairing_with(&g2.0) } @@ -1281,7 +1444,7 @@ fn same_ratio(g1: (G1, G1), g2: (G1::Pair, G1::Pair)) -> /// e(g, (as)*r1 + (bs)*r2 + (cs)*r3) = e(g^s, a*r1 + b*r2 + c*r3) /// /// ... with high probability. -fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { +pub(crate) fn merge_pairs(v1: &[G], v2: &[G]) -> (G, G) { use rand::thread_rng; use std::sync::Mutex; @@ -1384,7 +1547,7 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK /// Hashes to G2 using the first 32 bytes of `digest`. Panics if `digest` is less /// than 32 bytes. -fn hash_to_g2(digest: &[u8]) -> G2 { +pub(crate) fn hash_to_g2(digest: &[u8]) -> G2 { assert!(digest.len() >= 32); let mut seed = [0u8; 32]; @@ -1394,7 +1557,7 @@ fn hash_to_g2(digest: &[u8]) -> G2 { } /// Abstraction over a writer which hashes the data being written. -struct HashWriter { +pub(crate) struct HashWriter { writer: W, hasher: Blake2b, } diff --git a/small.rs b/small.rs new file mode 100644 index 000000000..5c201788d --- /dev/null +++ b/small.rs @@ -0,0 +1,502 @@ +use std::fmt::{self, Debug, Formatter}; +use std::fs::File; +use std::io::{self, BufReader, Read, Seek, SeekFrom, Write}; +use std::mem::size_of; + +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use ff::{Field, PrimeField}; +use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; +use log::{error, info}; +use paired::bls12_381::{Fr, G1 as G1Projective, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed}; +use rand::Rng; +use rayon::prelude::*; + +use crate::{hash_to_g2, HashWriter, merge_pairs, PrivateKey, PublicKey, same_ratio}; + +#[derive(Clone)] +pub struct MPCSmall { + // The Groth16 verification-key's deltas G1 and G2. For all non-initial parameters + // `delta_g1 == contributions.last().delta_after`. + pub(crate) delta_g1: G1Affine, + pub(crate) delta_g2: G2Affine, + + // The Groth16 parameter's h and l vectors. + pub(crate) h: Vec, + pub(crate) l: Vec, + + // The MPC parameter's constraint system digest and participant public-key set. + pub(crate) cs_hash: [u8; 64], + pub(crate) contributions: Vec, +} + +// Required by `assert_eq!()`. +impl Debug for MPCSmall { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("MPCSmall") + .field("delta_g1", &self.delta_g1) + .field("delta_g2", &self.delta_g2) + .field("h", &format!("", self.h.len())) + .field("l", &format!("", self.l.len())) + .field("cs_hash", &self.cs_hash.to_vec()) + .field("contributions", &format!("", self.contributions.len())) + .finish() + } +} + +impl PartialEq for MPCSmall { + fn eq(&self, other: &Self) -> bool { + self.h == other.h + && self.l == other.l + && self.delta_g1 == other.delta_g1 + && self.delta_g2 == other.delta_g2 + && self.cs_hash[..] == other.cs_hash[..] + && self.contributions == other.contributions + } +} + +impl MPCSmall { + pub fn contribute(&mut self, rng: &mut R) -> [u8; 64] { + let (pubkey, privkey) = keypair(rng, self); + + self.delta_g1 = self.delta_g1.mul(privkey.delta).into_affine(); + self.delta_g2 = self.delta_g2.mul(privkey.delta).into_affine(); + + let delta_inv = privkey.delta.inverse().expect("nonzero"); + + info!("phase2::MPCParameters::contribute() batch_exp of h"); + batch_exp(&mut self.h, delta_inv); + info!("phase2::MPCParameters::contribute() finished batch_exp of h"); + + info!("phase2::MPCParameters::contribute() batch_exp of l"); + batch_exp(&mut self.l, delta_inv); + info!("phase2::MPCParameters::contribute() finished batch_exp of l"); + + self.contributions.push(pubkey.clone()); + + { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + pubkey.write(&mut sink).unwrap(); + sink.into_hash() + } + } + + /// Deserialize these parameters. + pub fn read(mut reader: R) -> io::Result { + let delta_g1: G1Affine = read_g1(&mut reader)?; + let delta_g2: G2Affine = read_g2(&mut reader)?; + + let h_len = reader.read_u32::()? as usize; + let mut h = Vec::::with_capacity(h_len); + for _ in 0..h_len { + h.push(read_g1(&mut reader)?); + } + + let l_len = reader.read_u32::()? as usize; + let mut l = Vec::::with_capacity(l_len); + for _ in 0..l_len { + l.push(read_g1(&mut reader)?); + } + + let mut cs_hash = [0u8; 64]; + reader.read_exact(&mut cs_hash)?; + + let contributions_len = reader.read_u32::()? as usize; + let mut contributions = Vec::::with_capacity(contributions_len); + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut reader)?); + } + + info!( + "phase2::MPCSmall::read() read vector lengths: h={}, l={}, contributions={}", + h.len(), + l.len(), + contributions.len(), + ); + + Ok(MPCSmall { + delta_g1, + delta_g2, + h, + l, + cs_hash, + contributions, + }) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; + + writer.write_u32::(self.h.len() as u32)?; + for h in &*self.h { + writer.write_all(h.into_uncompressed().as_ref())?; + } + + writer.write_u32::(self.l.len() as u32)?; + for l in &*self.l { + writer.write_all(l.into_uncompressed().as_ref())?; + } + + writer.write_all(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + + Ok(()) + } +} + +fn keypair(rng: &mut R, prev: &MPCSmall) -> (PublicKey, PrivateKey) { + // Sample random delta + let delta: Fr = Fr::random(rng); + + // Compute delta s-pair in G1 + let s = G1Projective::random(rng).into_affine(); + let s_delta: G1Affine = s.mul(delta).into_affine(); + + // H(cs_hash | | s | s_delta) + let h = { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + + sink.write_all(&prev.cs_hash[..]).unwrap(); + for pubkey in &prev.contributions { + pubkey.write(&mut sink).unwrap(); + } + sink.write_all(s.into_uncompressed().as_ref()).unwrap(); + sink.write_all(s_delta.into_uncompressed().as_ref()) + .unwrap(); + + sink.into_hash() + }; + + // This avoids making a weird assumption about the hash into the + // group. + let transcript = h; + + // Compute delta s-pair in G2 + let r: G2Affine = hash_to_g2(&h).into_affine(); + let r_delta: G2Affine = r.mul(delta).into_affine(); + + ( + PublicKey { + delta_after: prev.delta_g1.mul(delta).into_affine(), + s, + s_delta, + r_delta, + transcript, + }, + PrivateKey { delta }, + ) +} + +// Multiplies each element of `bases` by `coeff` (`coeff` is the number of times each base is added +// to itself when the curve group is written additively). +fn batch_exp(bases: &mut [G1Affine], coeff: Fr) { + let coeff = coeff.into_repr(); + + let cpus = num_cpus::get(); + let chunk_size = if bases.len() < cpus { + 1 + } else { + bases.len() / cpus + }; + + let mut products = vec![G1Projective::zero(); bases.len()]; + + // Multiply each base by `coeff`. + crossbeam::thread::scope(|scope| { + for (bases, products) in bases + .chunks_mut(chunk_size) + .zip(products.chunks_mut(chunk_size)) + { + scope.spawn(move |_| { + let mut wnaf = Wnaf::new(); + + for (base, products) in bases.iter_mut().zip(products.iter_mut()) { + *products = wnaf.base(base.into_projective(), 1).scalar(coeff); + } + }); + } + }) + .unwrap(); + + // Normalize the projective products. + products + .par_chunks_mut(chunk_size) + .for_each(|batch| G1Projective::batch_normalization(batch)); + + // Convert the normalized projective points back to affine. + bases + .par_iter_mut() + .zip(products.par_iter()) + .for_each(|(affine, projective)| { + *affine = projective.into_affine(); + }); +} + +pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result<[u8; 64], ()> { + // The after params must contain exactly one additonal contribution. + if before.contributions.len() + 1 != after.contributions.len() { + error!( + "phase2::verify_contribution_small() non-sequential contributions: + before.contributions.len()={}, \ + after.contributions.len()={}", + before.contributions.len(), + after.contributions.len() + ); + return Err(()); + } + + // Previous participant public keys should not change. + if before.contributions[..] != after.contributions[..after.contributions.len() - 1] { + error!("phase2::verify_contribution_small() previous public keys have changed"); + return Err(()); + } + + let before_is_initial = before.contributions.len() == 0; + let after_pubkey = after.contributions.last().unwrap(); + + // Check that the before params' `delta_g1` and `delta_after` are the same value. + if before_is_initial { + if before.delta_g1 != G1Affine::one() || before.delta_g2 != G2Affine::one() { + error!("phase2::verify_contribution_small() initial params do not have identity deltas"); + } + } else { + let before_pubkey = before.contributions.last().unwrap(); + if before.delta_g1 != before_pubkey.delta_after { + error!("phase2::verify_contribution_small() before params' delta_g1 and delta_after are not equal"); + return Err(()); + } + }; + // Check that the after params' `delta_g1` and `delta_after` are the same value. + if after.delta_g1 != after_pubkey.delta_after { + error!("phase2::verify_contribution_small() after params' delta_g1 and delta_after are not equal"); + return Err(()); + } + + // h and l will change from the contribution, but should have same length. + if before.h.len() != after.h.len() { + error!("phase2::verify_contribution_small() length of h has changed"); + return Err(()); + } + if before.l.len() != after.l.len() { + error!("phase2::verify_contribution_small() length of l has changed"); + return Err(()); + } + + // cs_hash should be the same. + if before.cs_hash[..] != after.cs_hash[..] { + error!("phase2::verify_contribution_small() cs_hash has changed"); + return Err(()); + } + + // Calculate the expected after params transcript. + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + sink.write_all(&before.cs_hash[..]).unwrap(); + for pubkey in &before.contributions { + pubkey.write(&mut sink).unwrap(); + } + sink.write_all(after_pubkey.s.into_uncompressed().as_ref()).unwrap(); + sink.write_all(after_pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + let calculated_after_transcript = sink.into_hash(); + + // Check the after params transcript against its calculated transcript. + if &after_pubkey.transcript[..] != calculated_after_transcript.as_ref() { + error!("phase2::verify_contribution_small() inconsistent transcript"); + return Err(()); + } + + let after_r = hash_to_g2(&after_pubkey.transcript[..]).into_affine(); + + // Check the signature of knowledge. Check that the participant's r and s were shifted by the + // same factor. + if !same_ratio((after_r, after_pubkey.r_delta), (after_pubkey.s, after_pubkey.s_delta)) { + error!("phase2::verify_contribution_small() participant's r and s were shifted by different deltas"); + return Err(()); + } + + // Check that delta_g1 and r were shifted by the same factor. + if !same_ratio((before.delta_g1, after.delta_g1), (after_r, after_pubkey.r_delta)) { + error!("phase2::verify_contribution_small() participant's delta_g1 and r where shifted by different deltas"); + return Err(()); + } + + // Check that delta_g1 and delta_g2 were shifted by the same factor. + if !same_ratio((G1Affine::one(), after.delta_g1), (G2Affine::one(), after.delta_g2)) { + error!("phase2::verify_contribution_small() delta_g1 and delta_g2 were shifted by different deltas"); + return Err(()); + } + + // h and l queries should be updated with `delta^-1`. + if !same_ratio( + merge_pairs(&before.h, &after.h), + (after.delta_g2, before.delta_g2), // reversed for inverse + ) { + error!("phase2::verify_contribution_small() h was not updated by delta^-1"); + return Err(()); + } + if !same_ratio( + merge_pairs(&before.l, &after.l), + (after.delta_g2, before.delta_g2), // reversed for inverse + ) { + error!("phase2::verify_contribution_small() l was not updated by delta^-1"); + return Err(()); + } + + // Calculate the "after" participant's contribution hash. + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + after_pubkey.write(&mut sink).unwrap(); + Ok(sink.into_hash()) +} + +#[inline] +pub fn read_g1(mut reader: R) -> io::Result { + let mut affine_bytes = G1Uncompressed::empty(); + reader.read_exact(affine_bytes.as_mut())?; + let affine = affine_bytes.into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if affine.is_zero() { + let e = io::Error::new( + io::ErrorKind::InvalidData, + "deserialized G1Affine is point at infinity", + ); + Err(e) + } else { + Ok(affine) + } +} + +#[inline] +pub fn read_g2(mut reader: R) -> io::Result { + let mut affine_bytes = G2Uncompressed::empty(); + reader.read_exact(affine_bytes.as_mut())?; + let affine = affine_bytes.into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + if affine.is_zero() { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "deserialized G2Affine is point at infinity", + )) + } else { + Ok(affine) + } +} + +pub fn read_small_params_from_large_file(large_path: &str) -> io::Result { + /* + `MPCParameters` are serialized in the order: + vk.alpha_g1 + vk.beta_g1 + vk.beta_g2 + vk.gamma_g2 + vk.delta_g1 + vk.delta_g2 + vk.ic length (4 bytes) + vk.ic (G1) + h length (4 bytes) + h (G1) + l length (4 bytes) + l (G1) + a length (4 bytes) + a (G1) + b_g1 length (4 bytes) + b_g1 (G1) + b_g2 length (4 bytes) + b_g2 (G2) + cs_hash (64 bytes) + contributions length (4 bytes) + contributions (544 bytes per PublicKey) + */ + + let g1_size = size_of::() as u64; // 96 bytes + let g2_size = size_of::() as u64; // 192 bytes + + let mut file = File::open(large_path)?; + + // Read delta_g1, delta_g2, and ic's length. + let delta_g1_offset = g1_size + g1_size + g2_size + g2_size; // + vk.alpha_g1 + vk.beta_g1 + vk.beta_g2 + vk.gamma_g2 + file.seek(SeekFrom::Start(delta_g1_offset)).unwrap(); + let delta_g1 = read_g1(&mut file)?; + let delta_g2 = read_g2(&mut file)?; + let ic_len = file.read_u32::()? as u64; + + // Read h's length. + let h_len_offset = delta_g1_offset + g1_size + g2_size + 4 + ic_len * g1_size; // + vk.delta_g1 + vk.delta_g2 + ic length + ic + file.seek(SeekFrom::Start(h_len_offset)).unwrap(); + let h_len = file.read_u32::()? as u64; + + // Read l's length. + let l_len_offset = h_len_offset + 4 + h_len * g1_size; // + h length + h + file.seek(SeekFrom::Start(l_len_offset)).unwrap(); + let l_len = file.read_u32::()? as u64; + + // Read a's length. + let a_len_offset = l_len_offset + 4 + l_len * g1_size; // + l length + l + file.seek(SeekFrom::Start(a_len_offset)).unwrap(); + let a_len = file.read_u32::()? as u64; + + // Read b_g1's length. + let b_g1_len_offset = a_len_offset + 4 + a_len * g1_size; // + a length + a + file.seek(SeekFrom::Start(b_g1_len_offset)).unwrap(); + let b_g1_len = file.read_u32::()? as u64; + + // Read b_g2's length. + let b_g2_len_offset = b_g1_len_offset + 4 + b_g1_len * g1_size; // + b_g1 length + b_g1 + file.seek(SeekFrom::Start(b_g2_len_offset)).unwrap(); + let b_g2_len = file.read_u32::()? as u64; + + // Read cs_hash. + let cs_hash_offset = b_g2_len_offset + 4 + b_g2_len * g2_size; // + b_g2 length + b_g2 + file.seek(SeekFrom::Start(cs_hash_offset)).unwrap(); + let mut cs_hash = [0u8; 64]; + file.read_exact(&mut cs_hash)?; + + // Read contribution's length. + let contributions_len = file.read_u32::()? as u64; + + drop(file); + + // Read the (potentially large) h, l, and contributions arrays using buffered io. + let file = File::open(large_path)?; + let mut reader = BufReader::with_capacity(1024 * 1024, file); + + // Read h. + let h_offset = h_len_offset + 4; // + h length + reader.seek(SeekFrom::Start(h_offset)).unwrap(); + let mut h = Vec::::with_capacity(h_len as usize); + for _ in 0..h_len { + h.push(read_g1(&mut reader)?); + } + + // Read l. Skip l's length because it was already read. + let _ = reader.read_u32::()? as u64; + let mut l = Vec::::with_capacity(l_len as usize); + for _ in 0..l_len { + l.push(read_g1(&mut reader)?); + } + + // Read the contributions. + let contributions_offset = cs_hash_offset + 64 + 4; // + 64-byte cs_hash + contributions length + reader.seek(SeekFrom::Start(contributions_offset)).unwrap(); + let mut contributions = Vec::::with_capacity(contributions_len as usize); + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut reader)?); + } + + Ok(MPCSmall { + delta_g1, + delta_g2, + h, + l, + cs_hash, + contributions, + }) +} From e121dcd6814084f90e8e487b54e91f98b5032ff2 Mon Sep 17 00:00:00 2001 From: porcuquine Date: Sat, 20 Jun 2020 23:59:08 -0700 Subject: [PATCH 27/40] Support streaming and fast i/o in contribution. --- lib.rs | 30 ++-- small.rs | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 451 insertions(+), 53 deletions(-) diff --git a/lib.rs b/lib.rs index cb94753e7..65ba674bd 100644 --- a/lib.rs +++ b/lib.rs @@ -799,23 +799,18 @@ impl MPCParameters { for (base, projective) in bases.iter_mut().zip(projective.iter_mut()) { *projective = wnaf.base(base.into_projective(), 1).scalar(coeff); } + + C::Projective::batch_normalization(projective); + projective + .iter() + .zip(bases.iter_mut()) + .for_each(|(projective, affine)| { + *affine = projective.into_affine(); + }); }); } }) .unwrap(); - - // Perform batch normalization - projective - .par_chunks_mut(chunk_size) - .for_each(|p| C::Projective::batch_normalization(p)); - - // Turn it all back into affine points - projective - .par_iter() - .zip(bases.par_iter_mut()) - .for_each(|(projective, affine)| { - *affine = projective.into_affine(); - }); } let delta_inv = privkey.delta.inverse().expect("nonzero"); @@ -1131,7 +1126,14 @@ impl MPCParameters { "small params are internally inconsistent wrt. G1 deltas" ); - let MPCSmall { delta_g1, delta_g2, h, l, contributions, .. } = contrib; + let MPCSmall { + delta_g1, + delta_g2, + h, + l, + contributions, + .. + } = contrib; self.params.vk.delta_g1 = delta_g1; self.params.vk.delta_g2 = delta_g2; self.params.h = Arc::new(h); diff --git a/small.rs b/small.rs index 5c201788d..54cbb9559 100644 --- a/small.rs +++ b/small.rs @@ -1,21 +1,22 @@ use std::fmt::{self, Debug, Formatter}; use std::fs::File; -use std::io::{self, BufReader, Read, Seek, SeekFrom, Write}; +use std::io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::mem::size_of; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use ff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; use log::{error, info}; -use paired::bls12_381::{Fr, G1 as G1Projective, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed}; +use paired::bls12_381::{ + Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1 as G1Projective, +}; use rand::Rng; -use rayon::prelude::*; -use crate::{hash_to_g2, HashWriter, merge_pairs, PrivateKey, PublicKey, same_ratio}; +use crate::{hash_to_g2, merge_pairs, same_ratio, HashWriter, PrivateKey, PublicKey}; #[derive(Clone)] pub struct MPCSmall { - // The Groth16 verification-key's deltas G1 and G2. For all non-initial parameters + // The Groth16 verification-key's deltas G1 and G2. For all non-initial parameters // `delta_g1 == contributions.last().delta_after`. pub(crate) delta_g1: G1Affine, pub(crate) delta_g2: G2Affine, @@ -29,6 +30,364 @@ pub struct MPCSmall { pub(crate) contributions: Vec, } +pub struct Streamer<'a> { + delta_g1: G1Affine, + delta_g2: G2Affine, + h_len_offset: u64, + cs_hash: [u8; 64], + contributions: Vec, + path: &'a str, + read_raw: bool, + write_raw: bool, +} + +impl<'a> Streamer<'a> { + // Create a new `Streamer` from small params file. + pub fn new(path: &'a str, read_raw: bool, write_raw: bool) -> io::Result { + let mut file = File::open(path)?; + + let delta_g1: G1Affine = read_g1(&mut file)?; + let delta_g2: G2Affine = read_g2(&mut file)?; + let g1_size = size_of::(); + let g2_size = size_of::(); + + let chunk_element_read_size = if read_raw { + G1Affine::raw_fmt_size() + } else { + size_of::() + }; + let h_len_offset = g1_size + g2_size; + let h_len = file.read_u32::()? as usize; + file.seek(SeekFrom::Current((h_len * chunk_element_read_size) as i64))?; + + let l_len = file.read_u32::()? as usize; + file.seek(SeekFrom::Current((l_len * chunk_element_read_size) as i64))?; + let mut cs_hash = [0u8; 64]; + file.read_exact(&mut cs_hash)?; + + let contributions_len = file.read_u32::()? as usize; + let mut contributions = Vec::::with_capacity(contributions_len); + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut file)?); + } + + let streamer = Streamer { + delta_g1, + delta_g2, + h_len_offset: h_len_offset as u64, + cs_hash, + contributions, + path, + read_raw, + write_raw, + }; + + Ok(streamer) + } + + // Create a new `Streamer` from large params file. + pub fn new_from_large_file( + path: &'a str, + read_raw: bool, + write_raw: bool, + ) -> io::Result { + let mut file = File::open(path)?; + + /* + `MPCParameters` are serialized in the order: + vk.alpha_g1 + vk.beta_g1 + vk.beta_g2 + vk.gamma_g2 + vk.delta_g1 + vk.delta_g2 + vk.ic length (4 bytes) + vk.ic (G1) + h length (4 bytes) + h (G1) + l length (4 bytes) + l (G1) + a length (4 bytes) + a (G1) + b_g1 length (4 bytes) + b_g1 (G1) + b_g2 length (4 bytes) + b_g2 (G2) + cs_hash (64 bytes) + contributions length (4 bytes) + contributions (544 bytes per PublicKey) + */ + + let g1_size = size_of::() as u64; // 96 bytes + let g2_size = size_of::() as u64; // 192 bytes + + let chunk_element_read_size = if read_raw { + G1Affine::raw_fmt_size() as u64 + } else { + size_of::() as u64 + }; + + // Read delta_g1, delta_g2, and ic's length. + let delta_g1_offset = g1_size + g1_size + g2_size + g2_size; // + vk.alpha_g1 + vk.beta_g1 + vk.beta_g2 + vk.gamma_g2 + file.seek(SeekFrom::Start(delta_g1_offset)).unwrap(); + let delta_g1 = read_g1(&mut file)?; + let delta_g2 = read_g2(&mut file)?; + let ic_len = file.read_u32::()? as u64; + + // Read h's length. + let h_len_offset = delta_g1_offset + g1_size + g2_size + 4 + ic_len * g1_size; // + vk.delta_g1 + vk.delta_g2 + ic length + ic + file.seek(SeekFrom::Start(h_len_offset)).unwrap(); + let h_len = file.read_u32::()? as u64; + + // Read l's length. + let l_len_offset = h_len_offset + 4 + h_len * chunk_element_read_size; // + h length + h + file.seek(SeekFrom::Start(l_len_offset)).unwrap(); + let l_len = file.read_u32::()? as u64; + + // Read a's length. + let a_len_offset = l_len_offset + 4 + l_len * chunk_element_read_size; // + l length + l + file.seek(SeekFrom::Start(a_len_offset)).unwrap(); + let a_len = file.read_u32::()? as u64; + + // Read b_g1's length. + let b_g1_len_offset = a_len_offset + 4 + a_len * g1_size; // + a length + a + file.seek(SeekFrom::Start(b_g1_len_offset)).unwrap(); + let b_g1_len = file.read_u32::()? as u64; + + // Read b_g2's length. + let b_g2_len_offset = b_g1_len_offset + 4 + b_g1_len * g1_size; // + b_g1 length + b_g1 + file.seek(SeekFrom::Start(b_g2_len_offset)).unwrap(); + let b_g2_len = file.read_u32::()? as u64; + + // Read cs_hash. + let cs_hash_offset = b_g2_len_offset + 4 + b_g2_len * g2_size; // + b_g2 length + b_g2 + file.seek(SeekFrom::Start(cs_hash_offset)).unwrap(); + let mut cs_hash = [0u8; 64]; + file.read_exact(&mut cs_hash)?; + + // Read contribution's length. + let contributions_len = file.read_u32::()? as u64; + + // Read the contributions. + let contributions_offset = cs_hash_offset + 64 + 4; // + 64-byte cs_hash + contributions length + file.seek(SeekFrom::Start(contributions_offset)).unwrap(); + let mut contributions = Vec::::with_capacity(contributions_len as usize); + for _ in 0..contributions_len { + contributions.push(PublicKey::read(&mut file)?); + } + + let streamer = Streamer { + delta_g1, + delta_g2, + h_len_offset, + cs_hash, + contributions, + path, + read_raw, + write_raw, + }; + + Ok(streamer) + } + + pub fn contribute( + &mut self, + rng: &mut RR, + out_file: File, + chunk_size: usize, + ) -> io::Result<[u8; 64]> { + let chunk_element_read_size = if self.read_raw { + G1Affine::raw_fmt_size() + } else { + size_of::() + }; + let chunk_element_write_size = if self.write_raw { + G1Affine::raw_fmt_size() + } else { + size_of::() + }; + + let read_buf_size = chunk_element_read_size * chunk_size; + let write_buf_size = chunk_element_write_size * chunk_size; + + let file = File::open(self.path)?; + let mut reader = BufReader::with_capacity(read_buf_size, file); + let mut writer = BufWriter::with_capacity(write_buf_size, out_file); + + let (pubkey, privkey) = keypair(rng, &self.cs_hash, &self.contributions, &self.delta_g1); + + self.delta_g1 = self.delta_g1.mul(privkey.delta).into_affine(); + self.delta_g2 = self.delta_g2.mul(privkey.delta).into_affine(); + + let delta_inv = privkey.delta.inverse().expect("nonzero"); + + writer.write(self.delta_g1.into_uncompressed().as_ref())?; + writer.write(self.delta_g2.into_uncompressed().as_ref())?; + + { + reader.seek(SeekFrom::Start(self.h_len_offset))?; + let h_len = reader.read_u32::()?; + writer.write_u32::(h_len)?; + + let chunks_to_read = h_len as usize; + let mut chunks_read = 0; + let mut this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + + let mut h_chunk = Vec::::with_capacity(this_chunk_size); + + info!("phase2::MPCParameters::contribute() beginning streaming h"); + while this_chunk_size > 0 { + for _ in 0..this_chunk_size { + h_chunk.push(load_g1(&mut reader, self.read_raw)?); + } + chunks_read += this_chunk_size; + + batch_exp(&mut h_chunk, delta_inv); + + for h in &h_chunk { + dump_g1(&mut writer, h, self.write_raw)?; + } + + this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + h_chunk.truncate(0); + } + info!("phase2::MPCParameters::contribute() finished streaming h"); + } + { + let l_len = reader.read_u32::()?; + writer.write_u32::(l_len)?; + + let chunks_to_read = l_len as usize; + let mut chunks_read = 0; + let mut this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + + let mut l_chunk = Vec::::new(); + info!("phase2::MPCParameters::contribute() beginning streaming l"); + while this_chunk_size > 0 { + for _ in 0..this_chunk_size { + l_chunk.push(load_g1(&mut reader, self.read_raw)?); + } + chunks_read += this_chunk_size; + + batch_exp(&mut l_chunk, delta_inv); + + for l in &l_chunk { + dump_g1(&mut writer, l, self.write_raw)?; + } + + this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + l_chunk.truncate(0); + } + info!("phase2::MPCParameters::contribute() finished streaming l"); + } + + self.contributions.push(pubkey.clone()); + + writer.write(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + + { + let sink = io::sink(); + let mut sink = HashWriter::new(sink); + pubkey.write(&mut sink).unwrap(); + Ok(sink.into_hash()) + } + } + + /// Read from self and write out to `writer`, respecting own `read_raw` and `write_raw` flags but without otherwise changing data. + /// Useful for converting to and from raw format. + pub fn process(&mut self, out_file: File, chunk_size: usize) -> io::Result<()> { + let chunk_element_read_size = if self.read_raw { + G1Affine::raw_fmt_size() + } else { + size_of::() + }; + let chunk_element_write_size = if self.write_raw { + G1Affine::raw_fmt_size() + } else { + size_of::() + }; + + let read_buf_size = chunk_element_read_size * chunk_size; + let write_buf_size = chunk_element_write_size * chunk_size; + + let file = File::open(self.path)?; + let mut reader = BufReader::with_capacity(read_buf_size, file); + let mut writer = BufWriter::with_capacity(write_buf_size, out_file); + + writer.write(self.delta_g1.into_uncompressed().as_ref())?; + writer.write(self.delta_g2.into_uncompressed().as_ref())?; + + reader.seek(SeekFrom::Start(self.h_len_offset))?; + { + let h_len = reader.read_u32::()?; + writer.write_u32::(h_len)?; + + let chunks_to_read = h_len as usize; + let mut chunks_read = 0; + let mut this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + + let mut h_chunk = Vec::::with_capacity(this_chunk_size); + + info!("phase2::MPCParameters::convert() beginning streaming h"); + while this_chunk_size > 0 { + for _ in 0..this_chunk_size { + h_chunk.push(load_g1(&mut reader, self.read_raw)?); + } + chunks_read += this_chunk_size; + + for h in &h_chunk { + dump_g1(&mut writer, h, self.write_raw)?; + } + + this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + h_chunk.truncate(0); + } + info!("phase2::MPCParameters::convert() finished streaming h"); + } + + { + let l_len = reader.read_u32::()?; + writer.write_u32::(l_len)?; + + let chunks_to_read = l_len as usize; + let mut chunks_read = 0; + let mut this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + + let mut l_chunk = Vec::::new(); + info!("phase2::MPCParameters::convert() beginning streaming l"); + while this_chunk_size > 0 { + for _ in 0..this_chunk_size { + l_chunk.push(load_g1(&mut reader, self.read_raw)?); + } + chunks_read += this_chunk_size; + + for l in &l_chunk { + dump_g1(&mut writer, l, self.write_raw)?; + } + + this_chunk_size = usize::min(chunk_size, chunks_to_read - chunks_read); + l_chunk.truncate(0); + } + info!("phase2::MPCParameters::convert() finished streaming l"); + } + + writer.write(&self.cs_hash)?; + + writer.write_u32::(self.contributions.len() as u32)?; + + for pubkey in &self.contributions { + pubkey.write(&mut writer)?; + } + Ok(()) + } +} + // Required by `assert_eq!()`. impl Debug for MPCSmall { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -38,7 +397,10 @@ impl Debug for MPCSmall { .field("h", &format!("", self.h.len())) .field("l", &format!("", self.l.len())) .field("cs_hash", &self.cs_hash.to_vec()) - .field("contributions", &format!("", self.contributions.len())) + .field( + "contributions", + &format!("", self.contributions.len()), + ) .finish() } } @@ -56,7 +418,7 @@ impl PartialEq for MPCSmall { impl MPCSmall { pub fn contribute(&mut self, rng: &mut R) -> [u8; 64] { - let (pubkey, privkey) = keypair(rng, self); + let (pubkey, privkey) = keypair(rng, &self.cs_hash, &self.contributions, &self.delta_g1); self.delta_g1 = self.delta_g1.mul(privkey.delta).into_affine(); self.delta_g2 = self.delta_g2.mul(privkey.delta).into_affine(); @@ -82,20 +444,20 @@ impl MPCSmall { } /// Deserialize these parameters. - pub fn read(mut reader: R) -> io::Result { + pub fn read(mut reader: R, raw: bool) -> io::Result { let delta_g1: G1Affine = read_g1(&mut reader)?; let delta_g2: G2Affine = read_g2(&mut reader)?; - + let h_len = reader.read_u32::()? as usize; let mut h = Vec::::with_capacity(h_len); for _ in 0..h_len { - h.push(read_g1(&mut reader)?); + h.push(load_g1(&mut reader, raw)?); } let l_len = reader.read_u32::()? as usize; let mut l = Vec::::with_capacity(l_len); for _ in 0..l_len { - l.push(read_g1(&mut reader)?); + l.push(load_g1(&mut reader, raw)?); } let mut cs_hash = [0u8; 64]; @@ -126,7 +488,7 @@ impl MPCSmall { pub fn write(&self, mut writer: W) -> io::Result<()> { writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; - writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; + writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; writer.write_u32::(self.h.len() as u32)?; for h in &*self.h { @@ -149,7 +511,12 @@ impl MPCSmall { } } -fn keypair(rng: &mut R, prev: &MPCSmall) -> (PublicKey, PrivateKey) { +fn keypair( + rng: &mut R, + prev_cs_hash: &[u8; 64], + prev_contributions: &[PublicKey], + prev_delta_g1: &G1Affine, +) -> (PublicKey, PrivateKey) { // Sample random delta let delta: Fr = Fr::random(rng); @@ -162,8 +529,8 @@ fn keypair(rng: &mut R, prev: &MPCSmall) -> (PublicKey, PrivateKey) { let sink = io::sink(); let mut sink = HashWriter::new(sink); - sink.write_all(&prev.cs_hash[..]).unwrap(); - for pubkey in &prev.contributions { + sink.write_all(&prev_cs_hash[..]).unwrap(); + for pubkey in prev_contributions { pubkey.write(&mut sink).unwrap(); } sink.write_all(s.into_uncompressed().as_ref()).unwrap(); @@ -183,7 +550,7 @@ fn keypair(rng: &mut R, prev: &MPCSmall) -> (PublicKey, PrivateKey) { ( PublicKey { - delta_after: prev.delta_g1.mul(delta).into_affine(), + delta_after: prev_delta_g1.mul(delta).into_affine(), s, s_delta, r_delta, @@ -219,23 +586,19 @@ fn batch_exp(bases: &mut [G1Affine], coeff: Fr) { for (base, products) in bases.iter_mut().zip(products.iter_mut()) { *products = wnaf.base(base.into_projective(), 1).scalar(coeff); } + // Normalize the projective products. + G1Projective::batch_normalization(products); + + bases + .iter_mut() + .zip(products.iter()) + .for_each(|(affine, projective)| { + *affine = projective.into_affine(); + }); }); } }) .unwrap(); - - // Normalize the projective products. - products - .par_chunks_mut(chunk_size) - .for_each(|batch| G1Projective::batch_normalization(batch)); - - // Convert the normalized projective points back to affine. - bases - .par_iter_mut() - .zip(products.par_iter()) - .for_each(|(affine, projective)| { - *affine = projective.into_affine(); - }); } pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result<[u8; 64], ()> { @@ -263,7 +626,9 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< // Check that the before params' `delta_g1` and `delta_after` are the same value. if before_is_initial { if before.delta_g1 != G1Affine::one() || before.delta_g2 != G2Affine::one() { - error!("phase2::verify_contribution_small() initial params do not have identity deltas"); + error!( + "phase2::verify_contribution_small() initial params do not have identity deltas" + ); } } else { let before_pubkey = before.contributions.last().unwrap(); @@ -301,10 +666,12 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< for pubkey in &before.contributions { pubkey.write(&mut sink).unwrap(); } - sink.write_all(after_pubkey.s.into_uncompressed().as_ref()).unwrap(); - sink.write_all(after_pubkey.s_delta.into_uncompressed().as_ref()).unwrap(); + sink.write_all(after_pubkey.s.into_uncompressed().as_ref()) + .unwrap(); + sink.write_all(after_pubkey.s_delta.into_uncompressed().as_ref()) + .unwrap(); let calculated_after_transcript = sink.into_hash(); - + // Check the after params transcript against its calculated transcript. if &after_pubkey.transcript[..] != calculated_after_transcript.as_ref() { error!("phase2::verify_contribution_small() inconsistent transcript"); @@ -315,19 +682,28 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< // Check the signature of knowledge. Check that the participant's r and s were shifted by the // same factor. - if !same_ratio((after_r, after_pubkey.r_delta), (after_pubkey.s, after_pubkey.s_delta)) { + if !same_ratio( + (after_r, after_pubkey.r_delta), + (after_pubkey.s, after_pubkey.s_delta), + ) { error!("phase2::verify_contribution_small() participant's r and s were shifted by different deltas"); return Err(()); } // Check that delta_g1 and r were shifted by the same factor. - if !same_ratio((before.delta_g1, after.delta_g1), (after_r, after_pubkey.r_delta)) { + if !same_ratio( + (before.delta_g1, after.delta_g1), + (after_r, after_pubkey.r_delta), + ) { error!("phase2::verify_contribution_small() participant's delta_g1 and r where shifted by different deltas"); return Err(()); } // Check that delta_g1 and delta_g2 were shifted by the same factor. - if !same_ratio((G1Affine::one(), after.delta_g1), (G2Affine::one(), after.delta_g2)) { + if !same_ratio( + (G1Affine::one(), after.delta_g1), + (G2Affine::one(), after.delta_g2), + ) { error!("phase2::verify_contribution_small() delta_g1 and delta_g2 were shifted by different deltas"); return Err(()); } @@ -347,7 +723,7 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< error!("phase2::verify_contribution_small() l was not updated by delta^-1"); return Err(()); } - + // Calculate the "after" participant's contribution hash. let sink = io::sink(); let mut sink = HashWriter::new(sink); @@ -359,7 +735,8 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< pub fn read_g1(mut reader: R) -> io::Result { let mut affine_bytes = G1Uncompressed::empty(); reader.read_exact(affine_bytes.as_mut())?; - let affine = affine_bytes.into_affine() + let affine = affine_bytes + .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if affine.is_zero() { @@ -373,11 +750,30 @@ pub fn read_g1(mut reader: R) -> io::Result { } } +#[inline] +fn load_g1(mut reader: R, raw: bool) -> io::Result { + if raw { + G1Affine::read_raw(&mut reader) + } else { + read_g1(reader) + } +} + +#[inline] +fn dump_g1(mut writer: W, g1: &G1Affine, raw: bool) -> io::Result { + if raw { + g1.write_raw(&mut writer) + } else { + writer.write(g1.into_uncompressed().as_ref()) + } +} + #[inline] pub fn read_g2(mut reader: R) -> io::Result { let mut affine_bytes = G2Uncompressed::empty(); reader.read_exact(affine_bytes.as_mut())?; - let affine = affine_bytes.into_affine() + let affine = affine_bytes + .into_affine() .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; if affine.is_zero() { From afe512f9048dcbe8a12e9cc8603816a648e427a4 Mon Sep 17 00:00:00 2001 From: DrPeterVanNostrand Date: Wed, 12 Aug 2020 13:01:52 -0400 Subject: [PATCH 28/40] feat: add subgroup checks when deserializing small params --- small.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/small.rs b/small.rs index 54cbb9559..5f3c75c27 100644 --- a/small.rs +++ b/small.rs @@ -238,7 +238,7 @@ impl<'a> Streamer<'a> { info!("phase2::MPCParameters::contribute() beginning streaming h"); while this_chunk_size > 0 { for _ in 0..this_chunk_size { - h_chunk.push(load_g1(&mut reader, self.read_raw)?); + h_chunk.push(load_g1(&mut reader, self.read_raw, false)?); } chunks_read += this_chunk_size; @@ -265,7 +265,7 @@ impl<'a> Streamer<'a> { info!("phase2::MPCParameters::contribute() beginning streaming l"); while this_chunk_size > 0 { for _ in 0..this_chunk_size { - l_chunk.push(load_g1(&mut reader, self.read_raw)?); + l_chunk.push(load_g1(&mut reader, self.read_raw, false)?); } chunks_read += this_chunk_size; @@ -337,7 +337,7 @@ impl<'a> Streamer<'a> { info!("phase2::MPCParameters::convert() beginning streaming h"); while this_chunk_size > 0 { for _ in 0..this_chunk_size { - h_chunk.push(load_g1(&mut reader, self.read_raw)?); + h_chunk.push(load_g1(&mut reader, self.read_raw, false)?); } chunks_read += this_chunk_size; @@ -363,7 +363,7 @@ impl<'a> Streamer<'a> { info!("phase2::MPCParameters::convert() beginning streaming l"); while this_chunk_size > 0 { for _ in 0..this_chunk_size { - l_chunk.push(load_g1(&mut reader, self.read_raw)?); + l_chunk.push(load_g1(&mut reader, self.read_raw, false)?); } chunks_read += this_chunk_size; @@ -444,20 +444,20 @@ impl MPCSmall { } /// Deserialize these parameters. - pub fn read(mut reader: R, raw: bool) -> io::Result { + pub fn read(mut reader: R, raw: bool, check_raw: bool) -> io::Result { let delta_g1: G1Affine = read_g1(&mut reader)?; let delta_g2: G2Affine = read_g2(&mut reader)?; let h_len = reader.read_u32::()? as usize; let mut h = Vec::::with_capacity(h_len); for _ in 0..h_len { - h.push(load_g1(&mut reader, raw)?); + h.push(load_g1(&mut reader, raw, check_raw)?); } let l_len = reader.read_u32::()? as usize; let mut l = Vec::::with_capacity(l_len); for _ in 0..l_len { - l.push(load_g1(&mut reader, raw)?); + l.push(load_g1(&mut reader, raw, check_raw)?); } let mut cs_hash = [0u8; 64]; @@ -751,9 +751,9 @@ pub fn read_g1(mut reader: R) -> io::Result { } #[inline] -fn load_g1(mut reader: R, raw: bool) -> io::Result { +fn load_g1(mut reader: R, raw: bool, check_raw: bool) -> io::Result { if raw { - G1Affine::read_raw(&mut reader) + G1Affine::read_raw(&mut reader, check_raw) } else { read_g1(reader) } From ea160bb42c87cfe961c2237e883929a58c00e98f Mon Sep 17 00:00:00 2001 From: porcuquine Date: Wed, 12 Aug 2020 10:30:15 -0700 Subject: [PATCH 29/40] Use g1_read_raw_checked. --- small.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/small.rs b/small.rs index 5f3c75c27..0c9f995cf 100644 --- a/small.rs +++ b/small.rs @@ -753,7 +753,11 @@ pub fn read_g1(mut reader: R) -> io::Result { #[inline] fn load_g1(mut reader: R, raw: bool, check_raw: bool) -> io::Result { if raw { - G1Affine::read_raw(&mut reader, check_raw) + if check_raw { + G1Affine::read_raw_checked(&mut reader) + } else { + G1Affine::read_raw(&mut reader) + } } else { read_g1(reader) } From 721d2c1bd4631022759bbd4f4ae359b1c9a4ae8d Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 29 Oct 2020 13:02:01 +0100 Subject: [PATCH 30/40] feat: integrate blst backend --- lib.rs | 43 +++++++++++++++++++++---------------------- small.rs | 4 ++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/lib.rs b/lib.rs index 65ba674bd..799b5c862 100644 --- a/lib.rs +++ b/lib.rs @@ -2,8 +2,7 @@ //! //! ## Make your circuit //! -//! Grab the [`bellperson`](https://github.com/filecoin-project/bellman) and -//! [`paired`](https://github.com/filecoin-project/pairing) crates. Bellman +//! Grab the [`bellperson`](https://github.com/filecoin-project/bellman) crate. Bellman //! provides a trait called `Circuit`, which you must implement //! for your computation. //! @@ -11,12 +10,12 @@ //! a field element. //! //! ```rust -//! use paired::Engine; //! use ff::Field; //! use bellperson::{ //! Circuit, //! ConstraintSystem, //! SynthesisError, +//! bls::Engine, //! }; //! //! struct CubeRoot { @@ -81,7 +80,7 @@ //! let's create some parameters and make some proofs. //! //! ```rust,ignore -//! use paired::bls12_381::{Bls12, Fr}; +//! use bellperson::bls::{Bls12, Fr}; //! use bellperson::groth16::{ //! generate_random_parameters, //! create_random_proof, @@ -209,8 +208,8 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use ff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; use log::{error, info}; -use paired::{ - bls12_381::{Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1, G2}, +use bellperson::bls::{ + Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1Projective, G2Projective, Engine, PairingCurveAffine, }; use rand::{Rng, SeedableRng}; @@ -543,15 +542,15 @@ impl MPCParameters { let alpha_coeffs_g1 = Arc::new(alpha_coeffs_g1); let beta_coeffs_g1 = Arc::new(beta_coeffs_g1); - let mut ic = vec![G1::zero(); assembly.num_inputs]; + let mut ic = vec![G1Projective::zero(); assembly.num_inputs]; info!("phase2::MPCParameters::new() initialized ic vector"); - let mut l = vec![G1::zero(); assembly.num_aux]; + let mut l = vec![G1Projective::zero(); assembly.num_aux]; info!("phase2::MPCParameters::new() initialized l vector"); - let mut a_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut a_g1 = vec![G1Projective::zero(); assembly.num_inputs + assembly.num_aux]; info!("phase2::MPCParameters::new() initialized a_g1 vector"); - let mut b_g1 = vec![G1::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g1 = vec![G1Projective::zero(); assembly.num_inputs + assembly.num_aux]; info!("phase2::MPCParameters::new() initialized b_g1 vector"); - let mut b_g2 = vec![G2::zero(); assembly.num_inputs + assembly.num_aux]; + let mut b_g2 = vec![G2Projective::zero(); assembly.num_inputs + assembly.num_aux]; info!("phase2::MPCParameters::new() initialized b_g2 vector"); #[allow(clippy::too_many_arguments)] @@ -568,10 +567,10 @@ impl MPCParameters { ct: &[Vec<(Fr, usize)>], // Resulting evaluated QAP polynomials - a_g1: &mut [G1], - b_g1: &mut [G1], - b_g2: &mut [G2], - ext: &mut [G1], + a_g1: &mut [G1Projective], + b_g1: &mut [G1Projective], + b_g2: &mut [G2Projective], + ext: &mut [G1Projective], // Worker worker: &Worker, @@ -627,10 +626,10 @@ impl MPCParameters { } // Batch normalize - G1::batch_normalization(a_g1); - G1::batch_normalization(b_g1); - G2::batch_normalization(b_g2); - G1::batch_normalization(ext); + G1Projective::batch_normalization(a_g1); + G1Projective::batch_normalization(b_g1); + G2Projective::batch_normalization(b_g2); + G1Projective::batch_normalization(ext); }); } }); @@ -1508,7 +1507,7 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK let delta: Fr = Fr::random(rng); // Compute delta s-pair in G1 - let s = G1::random(rng).into_affine(); + let s = G1Projective::random(rng).into_affine(); let s_delta = s.mul(delta).into_affine(); // H(cs_hash | | s | s_delta) @@ -1549,13 +1548,13 @@ fn keypair(rng: &mut R, current: &MPCParameters) -> (PublicKey, PrivateK /// Hashes to G2 using the first 32 bytes of `digest`. Panics if `digest` is less /// than 32 bytes. -pub(crate) fn hash_to_g2(digest: &[u8]) -> G2 { +pub(crate) fn hash_to_g2(digest: &[u8]) -> G2Projective { assert!(digest.len() >= 32); let mut seed = [0u8; 32]; seed.copy_from_slice(&digest[..32]); - G2::random(&mut ChaChaRng::from_seed(seed)) + G2Projective::random(&mut ChaChaRng::from_seed(seed)) } /// Abstraction over a writer which hashes the data being written. diff --git a/small.rs b/small.rs index 0c9f995cf..ac991ede9 100644 --- a/small.rs +++ b/small.rs @@ -7,8 +7,8 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use ff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; use log::{error, info}; -use paired::bls12_381::{ - Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1 as G1Projective, +use bellperson::bls::{ + Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1Projective, }; use rand::Rng; From c02572e6dd08c1a485ffef9108eae623fa87f333 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 11:58:36 +0100 Subject: [PATCH 31/40] fixup: move phase2 src --- lib.rs => phase2/src/lib.rs | 0 small.rs => phase2/src/small.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename lib.rs => phase2/src/lib.rs (100%) rename small.rs => phase2/src/small.rs (100%) diff --git a/lib.rs b/phase2/src/lib.rs similarity index 100% rename from lib.rs rename to phase2/src/lib.rs diff --git a/small.rs b/phase2/src/small.rs similarity index 100% rename from small.rs rename to phase2/src/small.rs From a1a1c2806a29975e5b5e43061f3506b1936431f2 Mon Sep 17 00:00:00 2001 From: DrPeterVanNostrand Date: Tue, 16 Jun 2020 17:05:25 -0400 Subject: [PATCH 32/40] reduce param size during contribution stage --- large.rs | 76 ++++++++++++++++++ mimc/mod.rs | 112 ++++++++++++++++++++++++++ small.rs | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 large.rs create mode 100644 mimc/mod.rs create mode 100644 small.rs diff --git a/large.rs b/large.rs new file mode 100644 index 000000000..af821b9f8 --- /dev/null +++ b/large.rs @@ -0,0 +1,76 @@ +mod mimc; + +use std::path::Path; + +use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; +use ff::Field; +use paired::bls12_381::{Bls12, Fr}; +use phase21::{contains_contribution, MPCParameters, verify_contribution}; +use rand::thread_rng; + +use mimc::{mimc as mimc_hash, MiMCDemo, MIMC_ROUNDS}; + +// This test is marked as ignore because we haven't checked-in the phase1 file required for this +// test to pass when run via CI. To run this test you must have the correct phase1 params file in +// the top level directory of this crate. +#[test] +#[ignore] +fn test_large_params() { + assert!( + Path::new("./phase1radix2m10").exists(), + "the phase1 file `phase1radix2m10` must be in the crate's top level directory" + ); + + let rng = &mut thread_rng(); + + let constants = (0..MIMC_ROUNDS) + .map(|_| Fr::random(rng)) + .collect::>(); + + let circuit = MiMCDemo:: { + xl: None, + xr: None, + constants: &constants, + }; + + let mut params = MPCParameters::new(circuit).unwrap(); + let old_params = params.copy(); + params.contribute(rng); + + let first_contrib = verify_contribution(&old_params, ¶ms).expect("should verify"); + + let old_params = params.copy(); + params.contribute(rng); + + let second_contrib = verify_contribution(&old_params, ¶ms).expect("should verify"); + + let all_contributions = params + .verify(MiMCDemo:: { + xl: None, + xr: None, + constants: &constants, + }) + .unwrap(); + + assert!(contains_contribution(&all_contributions, &first_contrib)); + assert!(contains_contribution(&all_contributions, &second_contrib)); + + + // Create a Groth16 proof using the generated parameters and verfy that the proof is valid. + let groth_params = params.get_params(); + + // Generate a random preimage and compute the image. + let xl = Fr::random(rng); + let xr = Fr::random(rng); + let image = mimc_hash::(xl, xr, &constants); + + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants, + }; + let proof = create_random_proof(circuit, groth_params, rng).unwrap(); + + let pvk = prepare_verifying_key(&groth_params.vk); + assert!(verify_proof(&pvk, &proof, &[image]).unwrap()); +} diff --git a/mimc/mod.rs b/mimc/mod.rs new file mode 100644 index 000000000..adb39bdca --- /dev/null +++ b/mimc/mod.rs @@ -0,0 +1,112 @@ +use bellperson::{Circuit, ConstraintSystem, SynthesisError}; +use ff::Field; +use paired::Engine; + +pub const MIMC_ROUNDS: usize = 322; + +pub 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 +} + +pub struct MiMCDemo<'a, E: Engine> { + pub xl: Option, + pub xr: Option, + pub constants: &'a [E::Fr], +} + +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 tmp_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.square(); + e + }); + let 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 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 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(()) + } +} diff --git a/small.rs b/small.rs new file mode 100644 index 000000000..53a51e4bd --- /dev/null +++ b/small.rs @@ -0,0 +1,221 @@ +mod mimc; + +use std::fs::{File, remove_file}; +use std::io::{BufReader, BufWriter}; +use std::path::Path; + +use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; +use ff::Field; +use paired::bls12_381::{Bls12, Fr}; +use phase21::{MPCParameters, verify_contribution}; +use phase21::small::{MPCSmall, read_small_params_from_large_file, verify_contribution_small}; +use rand::{SeedableRng, thread_rng}; +use rand_chacha::ChaChaRng; + +use mimc::{mimc as mimc_hash, MiMCDemo, MIMC_ROUNDS}; + +// This test is marked as ignore because we haven't checked-in the phase1 file required for this +// test to pass when run via CI. To run this test you must have the correct phase1 params file in +// the top level directory of this crate. +#[test] +#[ignore] +fn test_mimc_small_params() { + assert!( + Path::new("./phase1radix2m10").exists(), + "the phase1 file `phase1radix2m10` must be in the crate's top level directory" + ); + + let constants = (0..MIMC_ROUNDS) + .map(|_| Fr::random(&mut thread_rng())) + .collect::>(); + + let circuit = MiMCDemo:: { + xl: None, + xr: None, + constants: &constants, + }; + + let mut rng_large = ChaChaRng::from_seed([0u8; 32]); + let mut rng_small = ChaChaRng::from_seed([0u8; 32]); + + // Create the initial params. + let initial_large = MPCParameters::new(circuit).unwrap(); + let initial_small = initial_large.copy_small(); + + let mut large_added = initial_large.copy(); + + // Make the first contributions. + let (first_large, first_large_contrib) = { + let mut params = initial_large.copy(); + let contrib = params.contribute(&mut rng_large); + (params, contrib) + }; + let (first_small, first_small_contrib) = { + let mut params = initial_small.clone(); + let contrib = params.contribute(&mut rng_small); + (params, contrib) + }; + + // Verify the first contributions. + assert_eq!( + &first_small_contrib[..], + &first_large_contrib[..], + "first small and large contributions are not equal" + ); + + let verified_large = verify_contribution(&initial_large, &first_large) + .expect("first large verify_contribution() failed"); + assert_eq!( + &first_large_contrib[..], + &verified_large[..], + "first large contribution does not match verified contribution" + ); + + let verified_small = verify_contribution_small(&initial_small, &first_small) + .expect("first small verify_contribution_small() failed"); + assert_eq!( + &first_small_contrib[..], + &verified_small[..], + "first small contribution does not match verified contribution" + ); + + // Verify that the first large and small params are consistent. + assert!(first_large.has_last_contrib(&first_small)); + large_added.add_contrib(first_small.clone()); + assert_eq!(large_added, first_large); + + // Make the second contributions. + let (second_large, second_large_contrib) = { + let mut params = first_large.copy(); + let contrib = params.contribute(&mut rng_large); + (params, contrib) + }; + let (second_small, second_small_contrib) = { + let mut params = first_small.clone(); + let contrib = params.contribute(&mut rng_small); + (params, contrib) + }; + + // Verify the second contributions. + assert_eq!( + &second_small_contrib[..], + &second_large_contrib[..], + "second small and large contributions are not equal" + ); + + let verified_large = verify_contribution(&first_large, &second_large) + .expect("second large verify_contribution() failed"); + assert_eq!( + &second_large_contrib[..], + &verified_large[..], + "second large contribution does not match verified contribution" + ); + + let verified_small = verify_contribution_small(&first_small, &second_small) + .expect("second small verify_contribution_small() failed"); + assert_eq!( + &second_small_contrib[..], + &verified_small[..], + "second small contribution does not match verified contribution" + ); + + // Verify that the second large and small params are consistent. + assert!(second_large.has_last_contrib(&second_small)); + large_added.add_contrib(second_small.clone()); + assert_eq!(large_added, second_large); + + // Verify large params against circuit. + let all_contributions = large_added + .verify(MiMCDemo:: { + xl: None, + xr: None, + constants: &constants, + }) + .unwrap(); + assert_eq!(all_contributions.len(), 2); + assert_eq!(&all_contributions[0][..], &first_large_contrib[..]); + assert_eq!(&all_contributions[1][..], &second_large_contrib[..]); + + // Verify the generated params against the circuit. + let groth_params = large_added.get_params(); + let pvk = prepare_verifying_key(&groth_params.vk); + // Generate a random preimage and compute the image. + let xl = Fr::random(&mut thread_rng()); + let xr = Fr::random(&mut thread_rng()); + let image = mimc_hash::(xl, xr, &constants); + // Create an instance of the circuit (with the witness). + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants, + }; + // Create a groth16 proof using the generated parameters. + let proof = create_random_proof(circuit, groth_params, &mut thread_rng()) + .expect("failed to create Groth16 proof using MPC params"); + assert!(verify_proof(&pvk, &proof, &[image]).unwrap()); +} + +// This test is marked as ignore because we haven't checked-in the phase1 file required for this +// test to pass when run via CI. To run this test you must have the correct phase1 params file in +// the top level directory of this crate. +#[test] +#[ignore] +fn test_small_file_io() { + const LARGE_PATH: &str = "./tests/large_params"; + const SMALL_PATH: &str = "./tests/small_params"; + + struct TestCleanup; + + impl Drop for TestCleanup { + fn drop(&mut self) { + remove_file(LARGE_PATH).unwrap(); + remove_file(SMALL_PATH).unwrap(); + } + } + + let _cleanup = TestCleanup; + + assert!( + Path::new("./phase1radix2m10").exists(), + "the phase1 file `phase1radix2m10` must be in the crate's top level directory" + ); + + let constants = (0..MIMC_ROUNDS) + .map(|_| Fr::random(&mut thread_rng())) + .collect::>(); + + let circuit = MiMCDemo:: { + xl: None, + xr: None, + constants: &constants, + }; + + let large_params = MPCParameters::new(circuit).unwrap(); + let small_params = large_params.copy_small(); + + { + let file = File::create(LARGE_PATH).unwrap(); + let mut writer = BufWriter::with_capacity(1024 * 1024, file); + large_params.write(&mut writer).unwrap(); + } + { + let file = File::create(SMALL_PATH).unwrap(); + let mut writer = BufWriter::with_capacity(1024 * 1024, file); + small_params.write(&mut writer).unwrap(); + } + + // Test small param deserialisation. + { + let file = File::open(SMALL_PATH).unwrap(); + let mut reader = BufReader::with_capacity(1024 * 1024, file); + let small_read = MPCSmall::read(&mut reader).unwrap(); + assert_eq!(small_read, small_params); + assert!(large_params.has_last_contrib(&small_read)); + }; + + // Test `read_small_params_from_large_file()`. + { + let small_read = read_small_params_from_large_file(LARGE_PATH).unwrap(); + assert_eq!(small_read, small_params); + } +} From 50e726449f8c4a37805085534ebbf22ec0151d84 Mon Sep 17 00:00:00 2001 From: porcuquine Date: Sat, 20 Jun 2020 23:59:08 -0700 Subject: [PATCH 33/40] Support streaming and fast i/o in contribution. --- small.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/small.rs b/small.rs index 53a51e4bd..f14b1c43d 100644 --- a/small.rs +++ b/small.rs @@ -1,15 +1,15 @@ mod mimc; -use std::fs::{File, remove_file}; +use std::fs::{remove_file, File}; use std::io::{BufReader, BufWriter}; use std::path::Path; use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; use ff::Field; use paired::bls12_381::{Bls12, Fr}; -use phase21::{MPCParameters, verify_contribution}; -use phase21::small::{MPCSmall, read_small_params_from_large_file, verify_contribution_small}; -use rand::{SeedableRng, thread_rng}; +use phase21::small::{read_small_params_from_large_file, verify_contribution_small, MPCSmall}; +use phase21::{verify_contribution, MPCParameters}; +use rand::{thread_rng, SeedableRng}; use rand_chacha::ChaChaRng; use mimc::{mimc as mimc_hash, MiMCDemo, MIMC_ROUNDS}; @@ -203,12 +203,12 @@ fn test_small_file_io() { let mut writer = BufWriter::with_capacity(1024 * 1024, file); small_params.write(&mut writer).unwrap(); } - + // Test small param deserialisation. { let file = File::open(SMALL_PATH).unwrap(); let mut reader = BufReader::with_capacity(1024 * 1024, file); - let small_read = MPCSmall::read(&mut reader).unwrap(); + let small_read = MPCSmall::read(&mut reader, false).unwrap(); assert_eq!(small_read, small_params); assert!(large_params.has_last_contrib(&small_read)); }; From 394cdf16991d84913e55bf3f36890788d4b90c5e Mon Sep 17 00:00:00 2001 From: porcuquine Date: Wed, 12 Aug 2020 10:30:15 -0700 Subject: [PATCH 34/40] Use g1_read_raw_checked. --- small.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/small.rs b/small.rs index f14b1c43d..e9dfa3780 100644 --- a/small.rs +++ b/small.rs @@ -208,7 +208,7 @@ fn test_small_file_io() { { let file = File::open(SMALL_PATH).unwrap(); let mut reader = BufReader::with_capacity(1024 * 1024, file); - let small_read = MPCSmall::read(&mut reader, false).unwrap(); + let small_read = MPCSmall::read(&mut reader, false, false).unwrap(); assert_eq!(small_read, small_params); assert!(large_params.has_last_contrib(&small_read)); }; From c101e8abd499dd5bc49d20640438e65691942119 Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Thu, 29 Oct 2020 13:02:01 +0100 Subject: [PATCH 35/40] feat: integrate blst backend --- large.rs | 2 +- mimc/mod.rs | 2 +- small.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/large.rs b/large.rs index af821b9f8..43c924819 100644 --- a/large.rs +++ b/large.rs @@ -4,7 +4,7 @@ use std::path::Path; use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; use ff::Field; -use paired::bls12_381::{Bls12, Fr}; +use bellperson::bls::{Bls12, Fr}; use phase21::{contains_contribution, MPCParameters, verify_contribution}; use rand::thread_rng; diff --git a/mimc/mod.rs b/mimc/mod.rs index adb39bdca..0e97c316d 100644 --- a/mimc/mod.rs +++ b/mimc/mod.rs @@ -1,6 +1,6 @@ use bellperson::{Circuit, ConstraintSystem, SynthesisError}; use ff::Field; -use paired::Engine; +use bellperson::bls::Engine; pub const MIMC_ROUNDS: usize = 322; diff --git a/small.rs b/small.rs index e9dfa3780..77e6f8175 100644 --- a/small.rs +++ b/small.rs @@ -6,7 +6,7 @@ use std::path::Path; use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; use ff::Field; -use paired::bls12_381::{Bls12, Fr}; +use bellperson::bls::{Bls12, Fr}; use phase21::small::{read_small_params_from_large_file, verify_contribution_small, MPCSmall}; use phase21::{verify_contribution, MPCParameters}; use rand::{thread_rng, SeedableRng}; From 300d51822a8f038c470c8cf51694a27775440394 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 11:59:24 +0100 Subject: [PATCH 36/40] fixup: move phase tests --- large.rs => phase2/tests/large.rs | 0 {mimc => phase2/tests/mimc}/mod.rs | 0 small.rs => phase2/tests/small.rs | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename large.rs => phase2/tests/large.rs (100%) rename {mimc => phase2/tests/mimc}/mod.rs (100%) rename small.rs => phase2/tests/small.rs (100%) diff --git a/large.rs b/phase2/tests/large.rs similarity index 100% rename from large.rs rename to phase2/tests/large.rs diff --git a/mimc/mod.rs b/phase2/tests/mimc/mod.rs similarity index 100% rename from mimc/mod.rs rename to phase2/tests/mimc/mod.rs diff --git a/small.rs b/phase2/tests/small.rs similarity index 100% rename from small.rs rename to phase2/tests/small.rs From 048602575a0f95185c938415b5d682088e3e4e66 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 12:06:29 +0100 Subject: [PATCH 37/40] integrate phase21 library --- phase2/Cargo.toml | 11 +++++++---- phase2/src/lib.rs | 18 +++++++++--------- phase2/src/main.rs | 20 +++++++++++--------- phase2/src/small.rs | 12 +++++------- phase2/tests/large.rs | 7 +++---- phase2/tests/mimc/mod.rs | 4 ++-- phase2/tests/small.rs | 10 ++++++---- 7 files changed, 43 insertions(+), 39 deletions(-) diff --git a/phase2/Cargo.toml b/phase2/Cargo.toml index 5642e8641..04405d5d3 100644 --- a/phase2/Cargo.toml +++ b/phase2/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" bellperson = { version = "0.12.0", default-features = false } filecoin-proofs = { version = "5.4.0", path = "../filecoin-proofs", default-features = false } storage-proofs = { version = "5.4.0", path = "../storage-proofs", default-features = false } -phase2 = { version = "0.11", package = "phase21", default-features = false } groupy = "0.3.1" log = "0.4.11" clap = "2.33.3" @@ -21,9 +20,13 @@ simplelog = "0.8.0" dialoguer = "0.7.1" hex = "0.4.2" blake2b_simd = "0.5.11" +fff = "0.2.3" +rayon = "1.5.0" +num_cpus = "1.13.0" +crossbeam = "0.8.0" [features] default = ["pairing", "gpu"] -gpu = ["bellperson/gpu", "filecoin-proofs/gpu", "storage-proofs/gpu", "phase2/gpu"] -pairing = ["bellperson/pairing", "filecoin-proofs/pairing", "storage-proofs/pairing", "phase2/pairing"] -blst = ["bellperson/blst", "filecoin-proofs/blst", "storage-proofs/blst", "phase2/blst"] +gpu = ["bellperson/gpu", "filecoin-proofs/gpu", "storage-proofs/gpu"] +pairing = ["bellperson/pairing", "filecoin-proofs/pairing", "storage-proofs/pairing"] +blst = ["bellperson/blst", "filecoin-proofs/blst", "storage-proofs/blst"] diff --git a/phase2/src/lib.rs b/phase2/src/lib.rs index 799b5c862..229003c0b 100644 --- a/phase2/src/lib.rs +++ b/phase2/src/lib.rs @@ -10,7 +10,7 @@ //! a field element. //! //! ```rust -//! use ff::Field; +//! use fff::Field; //! use bellperson::{ //! Circuit, //! ConstraintSystem, @@ -187,7 +187,7 @@ //! Great, now if you're happy, grab the Groth16 `Parameters` with //! `params.params()`, so that you can interact with the bellman APIs //! just as before. -#![deny(clippy::all, clippy::perf, clippy::correctness)] +#![deny(clippy::all, clippy::perf, clippy::correctness, rust_2018_idioms)] pub mod small; @@ -198,6 +198,10 @@ use std::{ sync::Arc, }; +use bellperson::bls::{ + Bls12, Engine, Fr, G1Affine, G1Projective, G1Uncompressed, G2Affine, G2Projective, + G2Uncompressed, PairingCurveAffine, +}; use bellperson::{ groth16::{Parameters, VerifyingKey}, multicore::Worker, @@ -205,13 +209,9 @@ use bellperson::{ }; use blake2b_simd::State as Blake2b; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use ff::{Field, PrimeField}; +use fff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; use log::{error, info}; -use bellperson::bls::{ - Bls12, Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1Projective, G2Projective, - Engine, PairingCurveAffine, -}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use rayon::prelude::*; @@ -382,7 +382,7 @@ pub struct MPCParameters { // Required by `assert_eq!()`. impl Debug for MPCParameters { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("MPCParameters") .field("params", &"") .field("cs_hash", &self.cs_hash.to_vec()) @@ -1177,7 +1177,7 @@ struct PublicKey { // Required by `assert_eq!()`. impl Debug for PublicKey { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("PublicKey") .field("delta_after", &self.delta_after) .field("s", &self.s) diff --git a/phase2/src/main.rs b/phase2/src/main.rs index 5055c6cf3..20762593d 100644 --- a/phase2/src/main.rs +++ b/phase2/src/main.rs @@ -1,3 +1,5 @@ +#![deny(clippy::all, clippy::perf, clippy::correctness, rust_2018_idioms)] + use std::fmt::{self, Debug, Formatter}; use std::fs::{self, File, OpenOptions}; use std::io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; @@ -13,6 +15,8 @@ use bellperson::bls::{Bls12, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed} use bellperson::groth16; use byteorder::{BigEndian, ReadBytesExt}; use clap::{App, AppSettings, Arg, ArgGroup, SubCommand}; +use filecoin_phase2::small::{read_small_params_from_large_file, MPCSmall, Streamer}; +use filecoin_phase2::MPCParameters; use filecoin_proofs::constants::*; use filecoin_proofs::parameters::{ setup_params, window_post_public_params, winning_post_public_params, @@ -23,8 +27,6 @@ use filecoin_proofs::types::{ use filecoin_proofs::with_shape; use groupy::{CurveAffine, EncodedPoint}; use log::{error, info, warn}; -use phase2::small::{read_small_params_from_large_file, MPCSmall, Streamer}; -use phase2::MPCParameters; use rand::rngs::OsRng; use rand::{RngCore, SeedableRng}; use rand_chacha::ChaChaRng; @@ -313,7 +315,7 @@ fn blank_sdr_poseidon_params(sector_size: u64) -> PoRepPu }; let public_params = as CompoundProof< - StackedDrg, + StackedDrg<'_, Tree, Sha256Hasher>, _, >>::setup(&setup_params) .expect("public param setup failed"); @@ -377,7 +379,7 @@ fn create_initial_params( let start = Instant::now(); let public_params = blank_sdr_poseidon_params(sector_size.as_u64()); let circuit = as CompoundProof< - StackedDrg, + StackedDrg<'_, Tree, Sha256Hasher>, _, >>::blank_circuit(&public_params); dt_create_circuit = start.elapsed().as_secs(); @@ -390,7 +392,7 @@ fn create_initial_params( let start = Instant::now(); let public_params = blank_winning_post_poseidon_params::(sector_size.as_u64()); let circuit = as CompoundProof< - FallbackPoSt, + FallbackPoSt<'_, Tree>, FallbackPoStCircuit, >>::blank_circuit(&public_params); dt_create_circuit = start.elapsed().as_secs(); @@ -403,7 +405,7 @@ fn create_initial_params( let start = Instant::now(); let public_params = blank_window_post_poseidon_params::(sector_size.as_u64()); let circuit = as CompoundProof< - FallbackPoSt, + FallbackPoSt<'_, Tree>, FallbackPoStCircuit, >>::blank_circuit(&public_params); dt_create_circuit = start.elapsed().as_secs(); @@ -814,7 +816,7 @@ fn verify_contribution( info!("verifying contribution"); let start_verification = Instant::now(); - let calculated_contrib = phase2::small::verify_contribution_small( + let calculated_contrib = filecoin_phase2::small::verify_contribution_small( &before_params.expect("before params failure"), &after_params.expect("after params failure"), ) @@ -901,7 +903,7 @@ struct FileInfo { } impl Debug for FileInfo { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("FileInfo") .field("delta_g1_offset", &self.delta_g1_offset) .field("delta_g1", &self.delta_g1) @@ -1065,7 +1067,7 @@ fn parameter_identifier(sector_size: u64, proof let public_params = blank_sdr_poseidon_params::(sector_size); as CacheableParameters< - StackedCircuit, + StackedCircuit<'_, Tree, Sha256Hasher>, _, >>::cache_identifier(&public_params) } diff --git a/phase2/src/small.rs b/phase2/src/small.rs index ac991ede9..882d8ba9d 100644 --- a/phase2/src/small.rs +++ b/phase2/src/small.rs @@ -3,13 +3,11 @@ use std::fs::File; use std::io::{self, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::mem::size_of; +use bellperson::bls::{Fr, G1Affine, G1Projective, G1Uncompressed, G2Affine, G2Uncompressed}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use ff::{Field, PrimeField}; +use fff::{Field, PrimeField}; use groupy::{CurveAffine, CurveProjective, EncodedPoint, Wnaf}; use log::{error, info}; -use bellperson::bls::{ - Fr, G1Affine, G1Uncompressed, G2Affine, G2Uncompressed, G1Projective, -}; use rand::Rng; use crate::{hash_to_g2, merge_pairs, same_ratio, HashWriter, PrivateKey, PublicKey}; @@ -43,7 +41,7 @@ pub struct Streamer<'a> { impl<'a> Streamer<'a> { // Create a new `Streamer` from small params file. - pub fn new(path: &'a str, read_raw: bool, write_raw: bool) -> io::Result { + pub fn new(path: &'a str, read_raw: bool, write_raw: bool) -> io::Result> { let mut file = File::open(path)?; let delta_g1: G1Affine = read_g1(&mut file)?; @@ -90,7 +88,7 @@ impl<'a> Streamer<'a> { path: &'a str, read_raw: bool, write_raw: bool, - ) -> io::Result { + ) -> io::Result> { let mut file = File::open(path)?; /* @@ -390,7 +388,7 @@ impl<'a> Streamer<'a> { // Required by `assert_eq!()`. impl Debug for MPCSmall { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("MPCSmall") .field("delta_g1", &self.delta_g1) .field("delta_g2", &self.delta_g2) diff --git a/phase2/tests/large.rs b/phase2/tests/large.rs index 43c924819..d04760c25 100644 --- a/phase2/tests/large.rs +++ b/phase2/tests/large.rs @@ -2,10 +2,10 @@ mod mimc; use std::path::Path; -use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; -use ff::Field; use bellperson::bls::{Bls12, Fr}; -use phase21::{contains_contribution, MPCParameters, verify_contribution}; +use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; +use fff::Field; +use filecoin_phase2::{contains_contribution, verify_contribution, MPCParameters}; use rand::thread_rng; use mimc::{mimc as mimc_hash, MiMCDemo, MIMC_ROUNDS}; @@ -55,7 +55,6 @@ fn test_large_params() { assert!(contains_contribution(&all_contributions, &first_contrib)); assert!(contains_contribution(&all_contributions, &second_contrib)); - // Create a Groth16 proof using the generated parameters and verfy that the proof is valid. let groth_params = params.get_params(); diff --git a/phase2/tests/mimc/mod.rs b/phase2/tests/mimc/mod.rs index 0e97c316d..913ff2866 100644 --- a/phase2/tests/mimc/mod.rs +++ b/phase2/tests/mimc/mod.rs @@ -1,6 +1,6 @@ -use bellperson::{Circuit, ConstraintSystem, SynthesisError}; -use ff::Field; use bellperson::bls::Engine; +use bellperson::{Circuit, ConstraintSystem, SynthesisError}; +use fff::Field; pub const MIMC_ROUNDS: usize = 322; diff --git a/phase2/tests/small.rs b/phase2/tests/small.rs index 77e6f8175..0907da3f4 100644 --- a/phase2/tests/small.rs +++ b/phase2/tests/small.rs @@ -4,11 +4,13 @@ use std::fs::{remove_file, File}; use std::io::{BufReader, BufWriter}; use std::path::Path; -use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; -use ff::Field; use bellperson::bls::{Bls12, Fr}; -use phase21::small::{read_small_params_from_large_file, verify_contribution_small, MPCSmall}; -use phase21::{verify_contribution, MPCParameters}; +use bellperson::groth16::{create_random_proof, prepare_verifying_key, verify_proof}; +use fff::Field; +use filecoin_phase2::small::{ + read_small_params_from_large_file, verify_contribution_small, MPCSmall, +}; +use filecoin_phase2::{verify_contribution, MPCParameters}; use rand::{thread_rng, SeedableRng}; use rand_chacha::ChaChaRng; From 103db21b0cdc88ff494361af0784d0f098a3ca85 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 12:08:51 +0100 Subject: [PATCH 38/40] add license and readme --- phase2/Cargo.toml | 5 +- phase2/LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++++++++++++ phase2/LICENSE-MIT | 23 +++++ phase2/README.md | 8 ++ 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 phase2/LICENSE-APACHE create mode 100644 phase2/LICENSE-MIT create mode 100644 phase2/README.md diff --git a/phase2/Cargo.toml b/phase2/Cargo.toml index 04405d5d3..a4847954f 100644 --- a/phase2/Cargo.toml +++ b/phase2/Cargo.toml @@ -1,10 +1,13 @@ [package] name = "filecoin-phase2" version = "0.1.0" +description = "Phase2 for Filecoin circuits" authors = ["dignifiedquire "] +license = "MIT OR Apache-2.0" edition = "2018" +repository = "https://github.com/filecoin-project/rust-fil-proofs" +readme = "README.md" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] bellperson = { version = "0.12.0", default-features = false } diff --git a/phase2/LICENSE-APACHE b/phase2/LICENSE-APACHE new file mode 100644 index 000000000..f8e5e5ea0 --- /dev/null +++ b/phase2/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/phase2/LICENSE-MIT b/phase2/LICENSE-MIT new file mode 100644 index 000000000..468cd79a8 --- /dev/null +++ b/phase2/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/phase2/README.md b/phase2/README.md new file mode 100644 index 000000000..b71c6f11b --- /dev/null +++ b/phase2/README.md @@ -0,0 +1,8 @@ +# Filecoin Phase2 + +Library and binary to run the 2nd phase of the trusted setup for the circuits. + +## License + +MIT or Apache 2.0 + From 06448d3dee10a2ad17a643e24777282674cefaa5 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 12:56:52 +0100 Subject: [PATCH 39/40] fixup: happy clippy --- phase2/src/small.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/phase2/src/small.rs b/phase2/src/small.rs index 882d8ba9d..760feda0b 100644 --- a/phase2/src/small.rs +++ b/phase2/src/small.rs @@ -219,8 +219,8 @@ impl<'a> Streamer<'a> { let delta_inv = privkey.delta.inverse().expect("nonzero"); - writer.write(self.delta_g1.into_uncompressed().as_ref())?; - writer.write(self.delta_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())?; { reader.seek(SeekFrom::Start(self.h_len_offset))?; @@ -281,7 +281,7 @@ impl<'a> Streamer<'a> { self.contributions.push(pubkey.clone()); - writer.write(&self.cs_hash)?; + writer.write_all(&self.cs_hash)?; writer.write_u32::(self.contributions.len() as u32)?; @@ -318,8 +318,8 @@ impl<'a> Streamer<'a> { let mut reader = BufReader::with_capacity(read_buf_size, file); let mut writer = BufWriter::with_capacity(write_buf_size, out_file); - writer.write(self.delta_g1.into_uncompressed().as_ref())?; - writer.write(self.delta_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())?; reader.seek(SeekFrom::Start(self.h_len_offset))?; { @@ -375,7 +375,7 @@ impl<'a> Streamer<'a> { info!("phase2::MPCParameters::convert() finished streaming l"); } - writer.write(&self.cs_hash)?; + writer.write_all(&self.cs_hash)?; writer.write_u32::(self.contributions.len() as u32)?; From 3cbc0280d1721b578bd0de6bae3f20ad676505d7 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Tue, 3 Nov 2020 16:28:19 +0100 Subject: [PATCH 40/40] fixup: linting --- phase2/src/small.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phase2/src/small.rs b/phase2/src/small.rs index 760feda0b..4ea40c5ba 100644 --- a/phase2/src/small.rs +++ b/phase2/src/small.rs @@ -618,7 +618,7 @@ pub fn verify_contribution_small(before: &MPCSmall, after: &MPCSmall) -> Result< return Err(()); } - let before_is_initial = before.contributions.len() == 0; + let before_is_initial = before.contributions.is_empty(); let after_pubkey = after.contributions.last().unwrap(); // Check that the before params' `delta_g1` and `delta_after` are the same value.