diff --git a/Cargo.lock b/Cargo.lock index 5f72fd12..a871dc1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "algebra" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", ] @@ -29,7 +29,7 @@ dependencies = [ [[package]] name = "algebra-core" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core-derive", "derivative", @@ -41,7 +41,7 @@ dependencies = [ [[package]] name = "algebra-core-derive" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "proc-macro2", "quote", @@ -95,7 +95,7 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "bench-utils" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#4ab4b17ecec7ac6c635b4873321f7015a308273a" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" [[package]] name = "bitflags" @@ -169,6 +169,7 @@ dependencies = [ "gumdrop", "hex 0.4.2", "hex-literal", + "memmap", "phase2", "r1cs-core", "r1cs-std", @@ -192,9 +193,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" [[package]] name = "byte-tools" @@ -310,12 +311,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "crossbeam" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" - [[package]] name = "crossbeam" version = "0.7.3" @@ -400,7 +395,7 @@ dependencies = [ [[package]] name = "crypto-primitives" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", "bench-utils", @@ -501,7 +496,7 @@ dependencies = [ [[package]] name = "ff-fft" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", "rand 0.7.3", @@ -543,7 +538,7 @@ dependencies = [ [[package]] name = "gm17" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", "bench-utils", @@ -557,7 +552,7 @@ dependencies = [ [[package]] name = "groth16" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", "bench-utils", @@ -664,9 +659,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" dependencies = [ "wasm-bindgen", ] @@ -789,11 +784,12 @@ dependencies = [ "algebra", "byteorder", "cfg-if", - "crossbeam 0.3.2", + "crossbeam", "groth16", "num_cpus", "powersoftau", "r1cs-core", + "r1cs-std", "rand 0.7.3", "rayon", "snark-utils", @@ -882,7 +878,7 @@ dependencies = [ [[package]] name = "r1cs-core" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra-core", "smallvec", @@ -891,7 +887,7 @@ dependencies = [ [[package]] name = "r1cs-std" version = "0.1.0" -source = "git+https://github.com/scipr-lab/zexe#5674630058833faca90b117f2a860c5380e2cff6" +source = "git+https://github.com/scipr-lab/zexe#317a272370156c32301f3930af23230a1fd5993a" dependencies = [ "algebra", "derivative", @@ -1018,9 +1014,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" -version = "1.3.5" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8900ebc1363efa7ea1c399ccc32daed870b4002651e0bed86e72d501ebbe0048" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ "aho-corasick", "memchr", @@ -1158,7 +1154,7 @@ dependencies = [ "algebra", "blake2", "criterion", - "crossbeam 0.7.3", + "crossbeam", "ff-fft", "num_cpus", "powersoftau", @@ -1186,9 +1182,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", "quote", @@ -1229,18 +1225,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" +checksum = "e3711fd1c4e75b3eff12ba5c40dba762b6b65c5476e8174c1a664772060c49bf" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +checksum = "ae2b85ba4c9aa32dd3343bd80eb8d22e9b54b7688c17ea3907f236885353b233" dependencies = [ "proc-macro2", "quote", @@ -1391,9 +1387,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" +checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1401,9 +1397,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" +checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" dependencies = [ "bumpalo", "lazy_static", @@ -1416,9 +1412,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" +checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1426,9 +1422,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" +checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ "proc-macro2", "quote", @@ -1439,15 +1435,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" +checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/bls-snark-setup/Cargo.toml b/bls-snark-setup/Cargo.toml index c985f6f6..96855724 100644 --- a/bls-snark-setup/Cargo.toml +++ b/bls-snark-setup/Cargo.toml @@ -21,3 +21,4 @@ rand = "0.7.3" thiserror = "1.0.11" hex = "0.4.2" tracing-subscriber = "0.2.3" +memmap = "0.7.0" diff --git a/bls-snark-setup/src/cli/contribute.rs b/bls-snark-setup/src/cli/contribute.rs index d040b168..94f0b3ad 100644 --- a/bls-snark-setup/src/cli/contribute.rs +++ b/bls-snark-setup/src/cli/contribute.rs @@ -1,5 +1,6 @@ use gumdrop::Options; -use phase2::chunked_groth16::contribute as chunked_contribute; +use memmap::MmapOptions; +use phase2::{chunked_groth16::contribute as chunked_contribute, keypair::PublicKey}; use rand::Rng; use snark_utils::Result; use std::fs::OpenOptions; @@ -23,13 +24,21 @@ pub struct ContributeOpts { } pub fn contribute(opts: &ContributeOpts, rng: &mut R) -> Result<()> { - let mut file = OpenOptions::new() + let file = OpenOptions::new() .read(true) .write(true) .open(&opts.data) .expect("could not open file for writing the new MPC parameters "); + let metadata = file.metadata()?; + // extend the file by 1 pubkey + file.set_len(metadata.len() + PublicKey::::size() as u64)?; + let mut file = unsafe { + MmapOptions::new() + .map_mut(&file) + .expect("unable to create a memory map for input") + }; - chunked_contribute::(&mut file, rng, opts.batch)?; + chunked_contribute::(&mut file, rng, opts.batch)?; Ok(()) } diff --git a/bls-snark-setup/src/cli/new.rs b/bls-snark-setup/src/cli/new.rs index 55ea307d..8a6dc777 100644 --- a/bls-snark-setup/src/cli/new.rs +++ b/bls-snark-setup/src/cli/new.rs @@ -8,6 +8,7 @@ use zexe_r1cs_std::test_constraint_counter::ConstraintCounter; use phase2::parameters::{circuit_to_qap, MPCParameters}; use snark_utils::{log_2, Groth16Params, Result, UseCompression}; +use memmap::MmapOptions; use std::fs::OpenOptions; #[derive(Debug, Options, Clone)] @@ -46,30 +47,36 @@ pub fn empty_circuit(opt: &NewOpts) -> (ValidatorSetUpdate, usize) { None, // The hashes are done over SW6 so no helper is provided for the setup ); - let num_constraints = { + let phase2_size = { let mut counter = ConstraintCounter::new(); valset .clone() .generate_constraints(&mut counter) .expect("could not calculate number of required constraints"); - let constraints = counter.num_constraints(); - let power = log_2(constraints) as u32; + let phase2_size = counter.num_aux + counter.num_inputs + counter.num_constraints; + let power = log_2(phase2_size) as u32; // get the nearest power of 2 - if constraints < 2usize.pow(power) { + if phase2_size < 2usize.pow(power) { 2usize.pow(power + 1) } else { - constraints + phase2_size } }; - (valset, num_constraints) + (valset, phase2_size) } pub fn new(opt: &NewOpts) -> Result<()> { - let mut phase1_transcript = OpenOptions::new() + let phase1_transcript = OpenOptions::new() .read(true) + .write(true) .open(&opt.phase1) .expect("could not read phase 1 transcript file"); + let mut phase1_transcript = unsafe { + MmapOptions::new() + .map_mut(&phase1_transcript) + .expect("unable to create a memory map for input") + }; let mut output = OpenOptions::new() .read(false) .write(true) @@ -77,7 +84,7 @@ pub fn new(opt: &NewOpts) -> Result<()> { .open(&opt.output) .expect("could not open file for writing the MPC parameters "); - let (valset, num_constraints) = empty_circuit(&opt); + let (valset, phase2_size) = empty_circuit(&opt); // Read `num_constraints` Lagrange coefficients from the Phase1 Powers of Tau which were // prepared for this step. This will fail if Phase 1 was too small. @@ -85,7 +92,7 @@ pub fn new(opt: &NewOpts) -> Result<()> { &mut phase1_transcript, COMPRESSION, 2usize.pow(opt.phase1_size), - num_constraints, + phase2_size, )?; // Convert it to a QAP diff --git a/e2e.sh b/e2e.sh index 7f9d7842..a33cae8e 100755 --- a/e2e.sh +++ b/e2e.sh @@ -24,12 +24,17 @@ $phase2 --response-fname response --phase2-fname processed --phase2-size $POWER ###### Phase 2 -$snark new --phase1 processed --output ceremony --num-epochs $NUM_EPOCHS --num-validators $NUM_VALIDATORS --phase1-size $POWER -cp ceremony initial -$snark contribute --data ceremony -$snark contribute --data ceremony +$snark new --phase1 processed --output initial_ceremony --num-epochs $NUM_EPOCHS --num-validators $NUM_VALIDATORS --phase1-size $POWER -$snark verify --before initial --after ceremony +cp initial_ceremony contribution1 +yes | $snark contribute --data contribution1 +$snark verify --before initial_ceremony --after contribution1 + +# a new contributor contributes +cp contribution1 contribution2 +yes | $snark contribute --data contribution2 +$snark verify --before contribution1 --after contribution2 +$snark verify --before initial_ceremony --after contribution2 # done! since `verify` passed, you can be sure that this will work -# as shown in the `mpc.rs` example +# as shown in the `mpc.rs` example \ No newline at end of file diff --git a/phase2/Cargo.toml b/phase2/Cargo.toml index 9caa5c5e..33a1b28d 100644 --- a/phase2/Cargo.toml +++ b/phase2/Cargo.toml @@ -22,10 +22,11 @@ zexe_r1cs_core = { git = "https://github.com/scipr-lab/zexe", package = "r1cs-co rand = "0.7.3" cfg-if = "0.1.10" num_cpus = "1" -crossbeam = "0.3" +crossbeam = "0.7" byteorder = "1.3.4" rayon = "1.3.0" [dev-dependencies] powersoftau = { path = "../powersoftau" } test-helpers = { path = "../test-helpers" } +zexe_r1cs_std = { git = "https://github.com/scipr-lab/zexe", package = "r1cs-std", version = "0.1.0" } \ No newline at end of file diff --git a/phase2/src/chunked_groth16.rs b/phase2/src/chunked_groth16.rs index 3cf3cba7..e810ee1b 100644 --- a/phase2/src/chunked_groth16.rs +++ b/phase2/src/chunked_groth16.rs @@ -3,7 +3,7 @@ //! Large MPCs can require >50GB of elements to be loaded in memory. This module provides //! utilities for operating directly on raw items which implement `Read`, `Write` and `Seek` //! such that contributing and verifying the MPC can be done in chunks which fit in memory. -use crate::keypair::{Keypair, PublicKey, PUBKEY_SIZE}; +use crate::keypair::{Keypair, PublicKey}; use crate::parameters::*; use byteorder::{BigEndian, WriteBytesExt}; use rand::Rng; @@ -136,11 +136,12 @@ pub fn verify( /// followed by the contributions array and the contributions hash), this will modify the /// Delta_g1, the VK's Delta_g2 and will update the H and L queries in place while leaving /// everything else unchanged -pub fn contribute( - buffer: &mut B, +pub fn contribute( + buffer: &mut [u8], rng: &mut R, batch_size: usize, ) -> Result<[u8; 64]> { + let buffer = &mut std::io::Cursor::new(buffer); // The VK is small so we read it directly from the start let mut vk = VerifyingKey::::deserialize(buffer)?; // leave beta_g1 unchanged @@ -187,10 +188,35 @@ pub fn contribute( skip_vec::(buffer)?; // Beta G1 skip_vec::(buffer)?; // Beta G2 - // update the h_query - chunked_mul_queries::(buffer, &delta_inv, batch_size)?; - // update the l_query - chunked_mul_queries::(buffer, &delta_inv, batch_size)?; + // The previous operations are all on small size elements so do them serially + // the `h` and `l` queries are relatively large, so we can get a nice speedup + // by performing the reads and writes in parallel + let h_query_len = u64::deserialize(buffer)? as usize; + let position = buffer.position() as usize; + let remaining = &mut buffer.get_mut()[position..]; + let (h, l) = remaining.split_at_mut(h_query_len * E::G1Affine::SERIALIZED_SIZE); + let l_query_len = u64::deserialize(&mut &*l)? as usize; + + // spawn 2 scoped threads to perform the contribution + crossbeam::scope(|s| { + s.spawn(|_| chunked_mul_queries::(h, h_query_len, &delta_inv, batch_size)); + s.spawn(|_| { + chunked_mul_queries::( + // since we read the l_query length we will pass the buffer + // after it + &mut l[u64::SERIALIZED_SIZE..], + l_query_len, + &delta_inv, + batch_size, + ) + }); + })?; + + // we processed the 2 elements via the raw buffer, so we have to modify the cursor accordingly + let pos = position + + (l_query_len + h_query_len) * E::G1Affine::SERIALIZED_SIZE + + u64::SERIALIZED_SIZE; + buffer.seek(SeekFrom::Start(pos as u64))?; // leave the cs_hash unchanged (64 bytes size) buffer.seek(SeekFrom::Current(64))?; @@ -200,7 +226,7 @@ pub fn contribute( // advance to where the next pubkey would be in the buffer and append it buffer.seek(SeekFrom::Current( - (PUBKEY_SIZE * contributions.len()) as i64, + (PublicKey::::size() * contributions.len()) as i64, ))?; public_key.write(buffer)?; @@ -218,13 +244,13 @@ fn skip_vec(buffer: &mut B) -> Result<()> { /// Multiplies a vector of affine elements by `element` in `batch_size` batches /// The first 8 bytes read from the buffer are the vector's length. The result /// is written back to the buffer in place -fn chunked_mul_queries( - buffer: &mut B, +fn chunked_mul_queries( + buffer: &mut [u8], + query_len: usize, element: &C::ScalarField, batch_size: usize, ) -> Result<()> { - // read total length - let query_len = u64::deserialize(buffer)? as usize; + let buffer = &mut std::io::Cursor::new(buffer); let iters = query_len / batch_size; let leftovers = query_len % batch_size; diff --git a/phase2/src/keypair.rs b/phase2/src/keypair.rs index 2e432c45..e70ad37b 100644 --- a/phase2/src/keypair.rs +++ b/phase2/src/keypair.rs @@ -7,7 +7,10 @@ use rand::Rng; use snark_utils::{hash_to_g2, Deserializer, HashWriter, Result, Serializer, UseCompression}; use std::fmt; use std::io::{self, Read, Write}; -use zexe_algebra::{AffineCurve, PairingEngine, ProjectiveCurve, UniformRand}; +use zexe_algebra::{ + AffineCurve, CanonicalSerialize, ConstantSerializedSize, PairingEngine, ProjectiveCurve, + UniformRand, +}; /// This needs to be destroyed by at least one participant /// for the final parameters to be secure. @@ -68,13 +71,17 @@ impl PublicKey { Ok(contributions) } + pub fn size() -> usize { + 3 * E::G1Affine::UNCOMPRESSED_SIZE + E::G2Affine::UNCOMPRESSED_SIZE + 64 + } + /// Serializes the key's **uncompressed** points to the provided /// writer pub fn write(&self, writer: &mut W) -> Result<()> { - writer.write_element(&self.delta_after, UseCompression::No)?; - writer.write_element(&self.s, UseCompression::No)?; - writer.write_element(&self.s_delta, UseCompression::No)?; - writer.write_element(&self.r_delta, UseCompression::No)?; + self.delta_after.serialize_uncompressed(writer)?; + self.s.serialize_uncompressed(writer)?; + self.s_delta.serialize_uncompressed(writer)?; + self.r_delta.serialize_uncompressed(writer)?; writer.write_all(&self.transcript)?; Ok(()) } diff --git a/phase2/src/lib.rs b/phase2/src/lib.rs index 8aaa47be..b3ff4d13 100644 --- a/phase2/src/lib.rs +++ b/phase2/src/lib.rs @@ -1,6 +1,6 @@ use cfg_if::cfg_if; -mod keypair; +pub mod keypair; pub mod parameters; mod polynomial; diff --git a/phase2/src/parameters.rs b/phase2/src/parameters.rs index 829a73b7..b24c1999 100644 --- a/phase2/src/parameters.rs +++ b/phase2/src/parameters.rs @@ -1,6 +1,6 @@ use snark_utils::*; use std::fmt; -use std::io::{self, Read, Seek, Write}; +use std::io::{self, Read, Write}; use zexe_algebra::{ AffineCurve, CanonicalDeserialize, CanonicalSerialize, Field, One, PairingEngine, @@ -44,9 +44,9 @@ impl PartialEq for MPCParameters { } impl MPCParameters { - pub fn new_from_buffer( + pub fn new_from_buffer( circuit: C, - transcript: &mut R, + transcript: &mut [u8], compressed: UseCompression, phase1_size: usize, phase2_size: usize, @@ -486,7 +486,7 @@ mod tests { contribution1.contribute(rng).unwrap(); let mut c1_serialized = vec![]; contribution1.write(&mut c1_serialized).unwrap(); - let mut c1_cursor = std::io::Cursor::new(c1_serialized); + let mut c1_cursor = std::io::Cursor::new(c1_serialized.clone()); // verify it against the previous step mpc.verify(&contribution1).unwrap(); @@ -497,8 +497,10 @@ mod tests { c1_cursor.set_position(0); // second contribution via batched method - let mut c2_cursor = c1_cursor.clone(); - contribute::(&mut c2_cursor, rng, 4).unwrap(); + let mut c2_buf = c1_serialized; + c2_buf.resize(c2_buf.len() + PublicKey::::size(), 0); // make the buffer larger by 1 contribution + contribute::(&mut c2_buf, rng, 4).unwrap(); + let mut c2_cursor = std::io::Cursor::new(c2_buf); c2_cursor.set_position(0); // verify it against the previous step @@ -549,7 +551,8 @@ mod tests { accumulator.alpha_tau_powers_g1, accumulator.beta_tau_powers_g1, accumulator.beta_g2, - ); + ) + .unwrap(); // this circuit requires 7 constraints, so a ceremony with size 8 is sufficient let c = TestCircuit::(None); diff --git a/phase2/tests/mpc.rs b/phase2/tests/mpc.rs index c82507b0..a3cd636c 100644 --- a/phase2/tests/mpc.rs +++ b/phase2/tests/mpc.rs @@ -11,18 +11,17 @@ use zexe_r1cs_core::ConstraintSynthesizer; fn generate_mpc_parameters(c: C, rng: &mut impl Rng) -> MPCParameters where E: PairingEngine, - C: ConstraintSynthesizer, + C: Clone + ConstraintSynthesizer, { - let powers = 5; + let powers = 6; // powers of tau let batch = 4; - let phase2_size = 8; let params = CeremonyParams::::new(powers, batch); let compressed = UseCompression::Yes; // make 1 power of tau contribution (assume powers of tau gets calculated properly) let (_, output, _, _) = setup_verify(compressed, compressed, ¶ms); let accumulator = BatchedAccumulator::deserialize(&output, compressed, ¶ms).unwrap(); - // prepare for 2^5 powers + // prepare only the first 32 powers (for whatever reason) let groth_params = Groth16Params::::new( 32, accumulator.tau_powers_g1, @@ -30,16 +29,19 @@ where accumulator.alpha_tau_powers_g1, accumulator.beta_tau_powers_g1, accumulator.beta_g2, - ); + ) + .unwrap(); // write the transcript to a file let mut writer = vec![]; groth_params.write(&mut writer, compressed).unwrap(); - let mut transcript = std::io::Cursor::new(writer); - transcript.set_position(0); - // read only the first 8 coefficients from the prepared 32 + // perform the MPC on only the amount of constraints required for the circuit + let mut counter = zexe_r1cs_std::test_constraint_counter::ConstraintCounter::new(); + c.clone().generate_constraints(&mut counter).unwrap(); + let phase2_size = counter.num_aux + counter.num_inputs + counter.num_constraints; + let mut mpc = - MPCParameters::::new_from_buffer(c, &mut transcript, compressed, 32, phase2_size) + MPCParameters::::new_from_buffer(c, writer.as_mut(), compressed, 32, phase2_size) .unwrap(); let before = mpc.clone(); diff --git a/powersoftau/src/bin/prepare_phase2.rs b/powersoftau/src/bin/prepare_phase2.rs index ca4bb2ab..72799283 100644 --- a/powersoftau/src/bin/prepare_phase2.rs +++ b/powersoftau/src/bin/prepare_phase2.rs @@ -38,7 +38,7 @@ struct PreparePhase2Opts { )] pub power: usize, #[options(help = "the size (in powers) of the phase 2 circuit", default = "21")] - pub phase2_size: usize, + pub phase2_size: u32, } fn main() -> Result<()> { @@ -89,13 +89,14 @@ fn prepare_phase2(opts: &PreparePhase2Opts) -> Result<( // Load the elements to the Groth16 utility let groth16_params = Groth16Params::::new( - opts.phase2_size, + 2usize.pow(opts.phase2_size), current_accumulator.tau_powers_g1, current_accumulator.tau_powers_g2, current_accumulator.alpha_tau_powers_g1, current_accumulator.beta_tau_powers_g1, current_accumulator.beta_g2, - ); + ) + .expect("could not create Groth16 Lagrange coefficients"); // Write the parameters groth16_params.write(&mut writer, UseCompression::No)?; diff --git a/snark-utils/src/errors.rs b/snark-utils/src/errors.rs index 6e4596bd..3a2b821a 100644 --- a/snark-utils/src/errors.rs +++ b/snark-utils/src/errors.rs @@ -26,6 +26,14 @@ pub enum Error { SynthesisError(#[from] SynthesisError), #[error("Phase 2 Error: {0}")] Phase2Error(#[from] Phase2Error), + #[error("Crossbeam error during while joining the thread")] + CrossBeamError, +} + +impl From> for Error { + fn from(_: Box) -> Error { + Error::CrossBeamError + } } #[derive(Debug, Error, PartialEq)] diff --git a/snark-utils/src/groth16_utils.rs b/snark-utils/src/groth16_utils.rs index b9f2e31c..4f89c1ea 100644 --- a/snark-utils/src/groth16_utils.rs +++ b/snark-utils/src/groth16_utils.rs @@ -3,7 +3,6 @@ use crate::{buffer_size, Deserializer, Result, Serializer, UseCompression}; use std::fmt::Debug; use std::io::Write; -use std::io::{Read, Seek, SeekFrom}; use zexe_algebra::{AffineCurve, PairingEngine, PrimeField, ProjectiveCurve}; use zexe_fft::EvaluationDomain; @@ -59,7 +58,7 @@ where /// x^i * (x^m - 1) for i in 0..=(m-2) a.k.a. /// x^(i + m) - x^i for i in 0..=(m-2) /// for radix2 evaluation domains -fn h_query_groth16(powers: Vec, degree: usize) -> Vec { +fn h_query_groth16(powers: &[C], degree: usize) -> Vec { cfg_into_iter!(0..degree - 1) .map(|i| powers[i + degree] + powers[i].neg()) .collect() @@ -79,31 +78,38 @@ impl Groth16Params { alpha_tau_powers_g1: Vec, beta_tau_powers_g1: Vec, beta_g2: E::G2Affine, - ) -> Self { + ) -> Result { // Create the evaluation domain let domain = EvaluationDomain::::new(phase2_size).expect("could not create domain"); - // Convert the accumulated powers to Lagrange coefficients - let coeffs_g1 = to_coeffs(&domain, &tau_powers_g1[0..phase2_size]); - let coeffs_g2 = to_coeffs(&domain, &tau_powers_g2[0..phase2_size]); - let alpha_coeffs_g1 = to_coeffs(&domain, &alpha_tau_powers_g1[0..phase2_size]); - let beta_coeffs_g1 = to_coeffs(&domain, &beta_tau_powers_g1[0..phase2_size]); - - // Calculate the query for the Groth16 proving system - // todo: we might want to abstract this so that it works generically - // over various proving systems in the future - let h_g1 = h_query_groth16(tau_powers_g1, phase2_size); - - Groth16Params { - alpha_g1: alpha_tau_powers_g1[0], - beta_g1: beta_tau_powers_g1[0], - beta_g2, - coeffs_g1, - coeffs_g2, - alpha_coeffs_g1, - beta_coeffs_g1, - h_g1, - } + Ok(crossbeam::scope(|s| -> Result<_> { + // Convert the accumulated powers to Lagrange coefficients + let coeffs_g1 = s.spawn(|_| to_coeffs(&domain, &tau_powers_g1[0..phase2_size])); + let coeffs_g2 = s.spawn(|_| to_coeffs(&domain, &tau_powers_g2[0..phase2_size])); + let alpha_coeffs_g1 = + s.spawn(|_| to_coeffs(&domain, &alpha_tau_powers_g1[0..phase2_size])); + let beta_coeffs_g1 = + s.spawn(|_| to_coeffs(&domain, &beta_tau_powers_g1[0..phase2_size])); + // Calculate the query for the Groth16 proving system + let h_g1 = s.spawn(|_| h_query_groth16(&tau_powers_g1, phase2_size)); + + let coeffs_g1 = coeffs_g1.join()?; + let coeffs_g2 = coeffs_g2.join()?; + let alpha_coeffs_g1 = alpha_coeffs_g1.join()?; + let beta_coeffs_g1 = beta_coeffs_g1.join()?; + let h_g1 = h_g1.join()?; + + Ok(Groth16Params { + alpha_g1: alpha_tau_powers_g1[0], + beta_g1: beta_tau_powers_g1[0], + beta_g2, + coeffs_g1, + coeffs_g2, + alpha_coeffs_g1, + beta_coeffs_g1, + h_g1, + }) + })??) } /// Writes the data structure to the provided writer, in compressed or uncompressed form. @@ -146,50 +152,94 @@ impl Groth16Params { /// Reads the first `num_constraints` coefficients from the provided processed /// Phase 1 transcript with size `phase1_size`. - pub fn read( - reader: &mut R, + pub fn read( + reader: &mut [u8], compressed: UseCompression, phase1_size: usize, num_constraints: usize, ) -> Result> { + let mut reader = std::io::Cursor::new(reader); let alpha_g1 = reader.read_element(compressed)?; let beta_g1 = reader.read_element(compressed)?; let beta_g2 = reader.read_element(compressed)?; - let g1_size = buffer_size::(compressed); - let g2_size = buffer_size::(compressed); + let position = reader.position() as usize; + let reader = &mut &reader.get_mut()[position..]; + + // Split the transcript in the appropriate sections + let (in_coeffs_g1, in_coeffs_g2, in_alpha_coeffs_g1, in_beta_coeffs_g1, in_h_g1) = + split_transcript::(reader, phase1_size, num_constraints, compressed); + + // Read all elements in parallel + // note: '??' is used for getting the result from the threaded operation, + // and then getting the result from the function inside the thread) + Ok(crossbeam::scope(|s| -> Result<_> { + let coeffs_g1 = s.spawn(|_| in_coeffs_g1.read_batch::(compressed)); + let coeffs_g2 = s.spawn(|_| in_coeffs_g2.read_batch::(compressed)); + let alpha_coeffs_g1 = + s.spawn(|_| in_alpha_coeffs_g1.read_batch::(compressed)); + let beta_coeffs_g1 = + s.spawn(|_| in_beta_coeffs_g1.read_batch::(compressed)); + let h_g1 = s.spawn(|_| in_h_g1.read_batch::(compressed)); + + let coeffs_g1 = coeffs_g1.join()??; + let coeffs_g2 = coeffs_g2.join()??; + let alpha_coeffs_g1 = alpha_coeffs_g1.join()??; + let beta_coeffs_g1 = beta_coeffs_g1.join()??; + let h_g1 = h_g1.join()??; + + Ok(Groth16Params { + alpha_g1, + beta_g1, + beta_g2, + coeffs_g1, + coeffs_g2, + alpha_coeffs_g1, + beta_coeffs_g1, + h_g1, + }) + })??) + } +} - let coeffs_g1 = reader.read_elements_exact(num_constraints, compressed)?; - skip(reader, phase1_size - num_constraints, g1_size)?; +/// Immutable slices with format [AlphaG1, BetaG1, BetaG2, CoeffsG1, CoeffsG2, AlphaCoeffsG1, BetaCoeffsG1, H_G1] +type SplitBuf<'a> = (&'a [u8], &'a [u8], &'a [u8], &'a [u8], &'a [u8]); - let coeffs_g2 = reader.read_elements_exact(num_constraints, compressed)?; - skip(reader, phase1_size - num_constraints, g2_size)?; +use crate::BatchDeserializer; - let alpha_coeffs_g1 = reader.read_elements_exact(num_constraints, compressed)?; - skip(reader, phase1_size - num_constraints, g1_size)?; +/// splits the transcript from phase 1 after it's been prepared and converted to coefficient form +fn split_transcript( + input: &[u8], + phase1_size: usize, + size: usize, + compressed: UseCompression, +) -> SplitBuf { + let g1_size = buffer_size::(compressed); + let g2_size = buffer_size::(compressed); - let beta_coeffs_g1 = reader.read_elements_exact(num_constraints, compressed)?; - skip(reader, phase1_size - num_constraints, g1_size)?; + // N elements per coefficient + let (coeffs_g1, others) = input.split_at(g1_size * size); + let (_, others) = others.split_at((phase1_size - size) * g1_size); - let h_g1 = reader.read_elements_exact(num_constraints - 1, compressed)?; + let (coeffs_g2, others) = others.split_at(g2_size * size); + let (_, others) = others.split_at((phase1_size - size) * g2_size); - Ok(Groth16Params { - alpha_g1, - beta_g1, - beta_g2, - coeffs_g1, - coeffs_g2, - alpha_coeffs_g1, - beta_coeffs_g1, - h_g1, - }) - } -} + let (alpha_coeffs_g1, others) = others.split_at(g1_size * size); + let (_, others) = others.split_at((phase1_size - size) * g1_size); -fn skip(reader: &mut R, num_els: usize, el_size: usize) -> Result<()> { - reader.seek(SeekFrom::Current((num_els * el_size) as i64))?; + let (beta_coeffs_g1, others) = others.split_at(g1_size * size); + let (_, others) = others.split_at((phase1_size - size) * g1_size); - Ok(()) + // N-1 for the h coeffs + let (h_coeffs, _) = others.split_at(g1_size * (size - 1)); + + ( + coeffs_g1, + coeffs_g2, + alpha_coeffs_g1, + beta_coeffs_g1, + h_coeffs, + ) } #[cfg(test)] @@ -245,13 +295,14 @@ mod tests { accumulator.alpha_tau_powers_g1, accumulator.beta_tau_powers_g1, accumulator.beta_g2, - ); + ) + .unwrap(); let mut writer = vec![]; groth_params.write(&mut writer, compat(compressed)).unwrap(); let mut reader = std::io::Cursor::new(writer); let deserialized = Groth16Params::::read( - &mut reader, + &mut reader.get_mut(), compat(compressed), prepared_phase1_size, prepared_phase1_size, // phase2_size == prepared phase1 size @@ -262,7 +313,7 @@ mod tests { let subset = prepared_phase1_size / 2; let deserialized_subset = Groth16Params::::read( - &mut reader, + &mut reader.get_mut(), compat(compressed), prepared_phase1_size, subset, // phase2 size is smaller than the prepared phase1 size