diff --git a/.gitignore b/.gitignore index a11fa303..3e0371ed 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ verifier.sol proof.json universal_setup.dat witness +witness.json # ZoKrates source files at the root of the repository /*.zok diff --git a/Cargo.lock b/Cargo.lock index 426d39b0..b7952272 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2036,9 +2036,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -2046,9 +2046,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel 0.5.6", "crossbeam-deque 0.8.2", @@ -2961,7 +2961,6 @@ dependencies = [ "ark-bls12-377", "byteorder", "cfg-if 0.1.10", - "csv", "derivative", "num-bigint 0.2.6", "pairing_ce", diff --git a/changelogs/unreleased/1289-dark64 b/changelogs/unreleased/1289-dark64 new file mode 100644 index 00000000..950a2f58 --- /dev/null +++ b/changelogs/unreleased/1289-dark64 @@ -0,0 +1 @@ +Change witness format to binary, optimize backend integration code to improve proving time \ No newline at end of file diff --git a/zokrates_ark/Cargo.toml b/zokrates_ark/Cargo.toml index c31dc8d3..97bd8692 100644 --- a/zokrates_ark/Cargo.toml +++ b/zokrates_ark/Cargo.toml @@ -28,7 +28,7 @@ ark-bls12-377 = { version = "^0.3.0", features = ["curve"], default-features = f ark-bw6-761 = { version = "^0.3.0", default-features = false } ark-gm17 = { version = "^0.3.0", default-features = false } ark-groth16 = { version = "^0.3.0", default-features = false } -ark-serialize = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = ["std"] } ark-relations = { version = "^0.3.0", default-features = false } ark-marlin = { git = "https://github.com/arkworks-rs/marlin", rev = "63cfd82", default-features = false } ark-poly = { version = "^0.3.0", default-features = false } diff --git a/zokrates_ark/src/gm17.rs b/zokrates_ark/src/gm17.rs index 337770b7..b756913a 100644 --- a/zokrates_ark/src/gm17.rs +++ b/zokrates_ark/src/gm17.rs @@ -40,11 +40,16 @@ impl NonUniversalBackend for Ark { } impl Backend for Ark { - fn generate_proof<'a, I: IntoIterator>, R: RngCore + CryptoRng>( + fn generate_proof< + 'a, + I: IntoIterator>, + R: std::io::Read, + G: RngCore + CryptoRng, + >( program: ProgIterator<'a, T, I>, witness: Witness, - proving_key: Vec, - rng: &mut R, + proving_key: R, + rng: &mut G, ) -> Proof { let computation = Computation::with_witness(program, witness); @@ -54,10 +59,9 @@ impl Backend for Ark { .map(parse_fr::) .collect::>(); - let pk = ProvingKey::<::ArkEngine>::deserialize_unchecked( - &mut proving_key.as_slice(), - ) - .unwrap(); + let pk = + ProvingKey::<::ArkEngine>::deserialize_unchecked(proving_key) + .unwrap(); let proof = ArkGM17::::prove(&pk, computation, rng).unwrap(); let proof_points = ProofPoints { @@ -136,7 +140,10 @@ mod tests { .unwrap(); let proof = >::generate_proof( - program, witness, keypair.pk, rng, + program, + witness, + keypair.pk.as_slice(), + rng, ); let ans = >::verify(keypair.vk, proof); @@ -160,8 +167,12 @@ mod tests { .execute(program.clone(), &[Bw6_761Field::from(42)]) .unwrap(); - let proof = - >::generate_proof(program, witness, keypair.pk, rng); + let proof = >::generate_proof( + program, + witness, + keypair.pk.as_slice(), + rng, + ); let ans = >::verify(keypair.vk, proof); assert!(ans); diff --git a/zokrates_ark/src/groth16.rs b/zokrates_ark/src/groth16.rs index 1a832f1e..3c7b4b80 100644 --- a/zokrates_ark/src/groth16.rs +++ b/zokrates_ark/src/groth16.rs @@ -4,6 +4,7 @@ use ark_groth16::{ ProvingKey, VerifyingKey, }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use std::io::Read; use zokrates_field::ArkFieldExtensions; use zokrates_field::Field; use zokrates_proof_systems::{Backend, NonUniversalBackend, Proof, SetupKeypair}; @@ -17,11 +18,16 @@ use zokrates_proof_systems::groth16::{ProofPoints, VerificationKey, G16}; use zokrates_proof_systems::Scheme; impl Backend for Ark { - fn generate_proof<'a, I: IntoIterator>, R: RngCore + CryptoRng>( + fn generate_proof< + 'a, + I: IntoIterator>, + R: Read, + G: RngCore + CryptoRng, + >( program: ProgIterator<'a, T, I>, witness: Witness, - proving_key: Vec, - rng: &mut R, + proving_key: R, + rng: &mut G, ) -> Proof { let computation = Computation::with_witness(program, witness); @@ -31,12 +37,12 @@ impl Backend for Ark { .map(parse_fr::) .collect::>(); - let pk = ProvingKey::<::ArkEngine>::deserialize_unchecked( - &mut proving_key.as_slice(), - ) - .unwrap(); + let pk = + ProvingKey::<::ArkEngine>::deserialize_unchecked(proving_key) + .unwrap(); let proof = Groth16::::prove(&pk, computation, rng).unwrap(); + let proof_points = ProofPoints { a: parse_g1::(&proof.a), b: parse_g2::(&proof.b), @@ -133,7 +139,10 @@ mod tests { .unwrap(); let proof = >::generate_proof( - program, witness, keypair.pk, rng, + program, + witness, + keypair.pk.as_slice(), + rng, ); let ans = >::verify(keypair.vk, proof); @@ -157,8 +166,12 @@ mod tests { .execute(program.clone(), &[Bw6_761Field::from(42)]) .unwrap(); - let proof = - >::generate_proof(program, witness, keypair.pk, rng); + let proof = >::generate_proof( + program, + witness, + keypair.pk.as_slice(), + rng, + ); let ans = >::verify(keypair.vk, proof); assert!(ans); diff --git a/zokrates_ark/src/lib.rs b/zokrates_ark/src/lib.rs index 322785c3..e466dd1f 100644 --- a/zokrates_ark/src/lib.rs +++ b/zokrates_ark/src/lib.rs @@ -9,7 +9,7 @@ use ark_relations::r1cs::{ }; use std::collections::BTreeMap; use zokrates_ast::common::Variable; -use zokrates_ast::ir::{CanonicalLinComb, ProgIterator, Statement, Witness}; +use zokrates_ast::ir::{LinComb, ProgIterator, Statement, Witness}; use zokrates_field::{ArkFieldExtensions, Field}; pub use self::parse::*; @@ -39,7 +39,7 @@ impl<'a, T, I: IntoIterator>> Computation<'a, T, I> { } fn ark_combination( - l: CanonicalLinComb, + l: LinComb, cs: &mut ConstraintSystem<<::ArkEngine as PairingEngine>::Fr>, symbols: &mut BTreeMap, witness: &mut Witness, @@ -113,24 +113,9 @@ impl<'a, T: Field + ArkFieldExtensions, I: IntoIterator> for statement in self.program.statements { if let Statement::Constraint(quad, lin, _) = statement { - let a = ark_combination( - quad.left.clone().into_canonical(), - &mut cs, - &mut symbols, - &mut witness, - ); - let b = ark_combination( - quad.right.clone().into_canonical(), - &mut cs, - &mut symbols, - &mut witness, - ); - let c = ark_combination( - lin.into_canonical(), - &mut cs, - &mut symbols, - &mut witness, - ); + let a = ark_combination(quad.left, &mut cs, &mut symbols, &mut witness); + let b = ark_combination(quad.right, &mut cs, &mut symbols, &mut witness); + let c = ark_combination(lin, &mut cs, &mut symbols, &mut witness); cs.enforce_constraint(a, b, c)?; } diff --git a/zokrates_ark/src/marlin.rs b/zokrates_ark/src/marlin.rs index 233f9ac5..0750c2b8 100644 --- a/zokrates_ark/src/marlin.rs +++ b/zokrates_ark/src/marlin.rs @@ -19,6 +19,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use digest::Digest; use rand_0_8::{CryptoRng, Error, RngCore, SeedableRng}; use sha3::Keccak256; +use std::io::Read; use std::marker::PhantomData; use zokrates_field::{ArkFieldExtensions, Field}; @@ -206,11 +207,16 @@ impl UniversalBackend for Ark } impl Backend for Ark { - fn generate_proof<'a, I: IntoIterator>, R: RngCore + CryptoRng>( + fn generate_proof< + 'a, + I: IntoIterator>, + R: Read, + G: RngCore + CryptoRng, + >( program: ProgIterator<'a, T, I>, witness: Witness, - proving_key: Vec, - rng: &mut R, + proving_key: R, + rng: &mut G, ) -> Proof { let computation = Computation::with_witness(program, witness); @@ -220,7 +226,7 @@ impl Backend for Ark { T::ArkEngine, DensePolynomial<<::ArkEngine as PairingEngine>::Fr>, >, - >::deserialize_unchecked(&mut proving_key.as_slice()) + >::deserialize_unchecked(proving_key) .unwrap(); let public_inputs = computation.public_inputs_values(); @@ -418,7 +424,10 @@ mod tests { .unwrap(); let proof = >::generate_proof( - program, witness, keypair.pk, rng, + program, + witness, + keypair.pk.as_slice(), + rng, ); let ans = >::verify(keypair.vk, proof); @@ -454,7 +463,10 @@ mod tests { .unwrap(); let proof = >::generate_proof( - program, witness, keypair.pk, rng, + program, + witness, + keypair.pk.as_slice(), + rng, ); let ans = >::verify(keypair.vk, proof); diff --git a/zokrates_ast/Cargo.toml b/zokrates_ast/Cargo.toml index 92cab53b..b135b06f 100644 --- a/zokrates_ast/Cargo.toml +++ b/zokrates_ast/Cargo.toml @@ -14,7 +14,6 @@ zokrates_pest_ast = { version = "0.3.0", path = "../zokrates_pest_ast" } cfg-if = "0.1" zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false } serde = { version = "1.0", features = ["derive"] } -csv = "1" serde_cbor = "0.11.2" num-bigint = { version = "0.2", default-features = false } serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/zokrates_ast/src/common/embed.rs b/zokrates_ast/src/common/embed.rs index 58294142..3eeed58c 100644 --- a/zokrates_ast/src/common/embed.rs +++ b/zokrates_ast/src/common/embed.rs @@ -1,5 +1,5 @@ use crate::common::{Parameter, RuntimeError, Solver, Variable}; -use crate::flat::{flat_expression_from_bits, flat_expression_from_variable_summands}; +use crate::flat::flat_expression_from_bits; use crate::flat::{FlatDirective, FlatExpression, FlatFunctionIterator, FlatStatement}; use crate::typed::types::{ ConcreteGenericsAssignment, DeclarationConstant, DeclarationSignature, DeclarationType, @@ -17,6 +17,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "bellman")] { use pairing_ce::bn256::Bn256; use zokrates_embed::{bellman::{from_bellman, generate_sha256_round_constraints}}; + use crate::flat::flat_expression_from_variable_summands; } } diff --git a/zokrates_ast/src/common/variable.rs b/zokrates_ast/src/common/variable.rs index 983e9e17..ab460623 100644 --- a/zokrates_ast/src/common/variable.rs +++ b/zokrates_ast/src/common/variable.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt; +use std::io::{Read, Write}; // A variable in a constraint system // id > 0 for intermediate variables @@ -33,28 +34,18 @@ impl Variable { (self.id as usize) - 1 } - pub fn try_from_human_readable(s: &str) -> Result { - if s == "~one" { - return Ok(Variable::one()); - } + pub fn write(&self, mut writer: W) -> std::io::Result<()> { + writer.write_all(&self.id.to_le_bytes())?; + Ok(()) + } - let mut public = s.split("~out_"); - match public.nth(1) { - Some(v) => { - let v = v.parse().map_err(|_| s)?; - Ok(Variable::public(v)) - } - None => { - let mut private = s.split('_'); - match private.nth(1) { - Some(v) => { - let v = v.parse().map_err(|_| s)?; - Ok(Variable::new(v)) - } - None => Err(s), - } - } - } + pub fn read(mut reader: R) -> std::io::Result { + let mut buf = [0; std::mem::size_of::()]; + reader.read_exact(&mut buf)?; + + Ok(Variable { + id: isize::from_le_bytes(buf), + }) } } diff --git a/zokrates_ast/src/ir/witness.rs b/zokrates_ast/src/ir/witness.rs index 366c62d8..70da540c 100644 --- a/zokrates_ast/src/ir/witness.rs +++ b/zokrates_ast/src/ir/witness.rs @@ -41,54 +41,44 @@ impl Witness { Witness(BTreeMap::new()) } - pub fn write(&self, writer: W) -> io::Result<()> { - let mut wtr = csv::WriterBuilder::new() - .delimiter(b' ') - .flexible(true) - .has_headers(false) - .from_writer(writer); + pub fn write(&self, mut writer: W) -> io::Result<()> { + let length = self.0.len(); + writer.write_all(&length.to_le_bytes())?; - // Write each line of the witness to the file for (variable, value) in &self.0 { - wtr.serialize((variable.to_string(), value.to_dec_string()))?; + variable.write(&mut writer)?; + value.write(&mut writer)?; } - Ok(()) } pub fn read(mut reader: R) -> io::Result { - let mut rdr = csv::ReaderBuilder::new() - .delimiter(b' ') - .flexible(true) - .has_headers(false) - .from_reader(&mut reader); + let mut witness = Self::empty(); - let map = rdr - .deserialize::<(String, String)>() - .map(|r| { - r.map(|(variable, value)| { - let variable = Variable::try_from_human_readable(&variable).map_err(|why| { - io::Error::new( - io::ErrorKind::Other, - format!("Invalid variable in witness: {}", why), - ) - })?; - let value = T::try_from_dec_str(&value).map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - format!("Invalid value in witness: {}", value), - ) - })?; - Ok((variable, value)) - }) - .map_err(|e| match e.into_kind() { - csv::ErrorKind::Io(e) => e, - e => io::Error::new(io::ErrorKind::Other, format!("{:?}", e)), - })? - }) - .collect::>>()?; + let mut buf = [0; std::mem::size_of::()]; + reader.read_exact(&mut buf)?; - Ok(Witness(map)) + let length: usize = usize::from_le_bytes(buf); + + for _ in 0..length { + let var = Variable::read(&mut reader)?; + let val = T::read(&mut reader)?; + + witness.insert(var, val); + } + + Ok(witness) + } + + pub fn write_json(&self, writer: W) -> io::Result<()> { + let map = self + .0 + .iter() + .map(|(k, v)| (k.to_string(), serde_json::json!(v.to_dec_string()))) + .collect::>(); + + serde_json::to_writer_pretty(writer, &map)?; + Ok(()) } } @@ -138,32 +128,29 @@ mod tests { } #[test] - fn wrong_value() { - let mut buff = Cursor::new(vec![]); + fn serialize_json() { + let w = Witness( + vec![ + (Variable::new(42), Bn128Field::from(42)), + (Variable::public(8), Bn128Field::from(8)), + (Variable::one(), Bn128Field::from(1)), + ] + .into_iter() + .collect(), + ); - buff.write_all("_1 123bug".as_ref()).unwrap(); - buff.set_position(0); + let mut buf = Cursor::new(vec![]); + w.write_json(&mut buf).unwrap(); - assert!(Witness::::read(buff).is_err()); - } - - #[test] - fn wrong_variable() { - let mut buff = Cursor::new(vec![]); - - buff.write_all("_1bug 123".as_ref()).unwrap(); - buff.set_position(0); - - assert!(Witness::::read(buff).is_err()); - } - - #[test] - fn not_csv() { - let mut buff = Cursor::new(vec![]); - buff.write_all("whatwhat".as_ref()).unwrap(); - buff.set_position(0); - - assert!(Witness::::read(buff).is_err()); + let output = String::from_utf8(buf.into_inner()).unwrap(); + assert_eq!( + output.as_str(), + r#"{ + "~out_8": "8", + "~one": "1", + "_42": "42" +}"# + ) } } } diff --git a/zokrates_bellman/Cargo.toml b/zokrates_bellman/Cargo.toml index 9d8fff44..fb4c70fe 100644 --- a/zokrates_bellman/Cargo.toml +++ b/zokrates_bellman/Cargo.toml @@ -9,7 +9,7 @@ wasm = ["bellman/nolog", "bellman/wasm"] multicore = ["bellman/multicore", "phase2/multicore"] [dependencies] -zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false } +zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false, features = ["bellman"] } zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false } zokrates_proof_systems = { version = "0.1", path = "../zokrates_proof_systems", default-features = false } diff --git a/zokrates_bellman/src/groth16.rs b/zokrates_bellman/src/groth16.rs index 1eae3b46..619d95e7 100644 --- a/zokrates_bellman/src/groth16.rs +++ b/zokrates_bellman/src/groth16.rs @@ -20,14 +20,19 @@ use zokrates_proof_systems::groth16::{ProofPoints, VerificationKey, G16}; use zokrates_proof_systems::Scheme; impl Backend for Bellman { - fn generate_proof<'a, I: IntoIterator>, R: RngCore + CryptoRng>( + fn generate_proof< + 'a, + I: IntoIterator>, + R: Read, + G: RngCore + CryptoRng, + >( program: ProgIterator<'a, T, I>, witness: Witness, - proving_key: Vec, - rng: &mut R, + proving_key: R, + rng: &mut G, ) -> Proof { let computation = Computation::with_witness(program, witness); - let params = Parameters::read(proving_key.as_slice(), true).unwrap(); + let params = Parameters::read(proving_key, true).unwrap(); let public_inputs: Vec = computation .public_inputs_values() @@ -108,7 +113,7 @@ impl MpcBackend for Bellman { } fn contribute( - params: &mut R, + params: R, rng: &mut G, output: &mut W, ) -> Result<[u8; 64], String> { @@ -124,8 +129,8 @@ impl MpcBackend for Bellman { Ok(hash) } - fn verify<'a, P: Read, R: Read, I: IntoIterator>>( - params: &mut P, + fn verify<'a, R: Read, I: IntoIterator>>( + params: R, program: ProgIterator<'a, T, I>, phase1_radix: &mut R, ) -> Result, String> { @@ -140,7 +145,7 @@ impl MpcBackend for Bellman { Ok(hashes) } - fn export_keypair(params: &mut R) -> Result, String> { + fn export_keypair(params: R) -> Result, String> { let params = MPCParameters::::read(params, true).map_err(|e| e.to_string())?; @@ -227,7 +232,10 @@ mod tests { .unwrap(); let proof = >::generate_proof( - program, witness, keypair.pk, rng, + program, + witness, + keypair.pk.as_slice(), + rng, ); let ans = >::verify(keypair.vk, proof); diff --git a/zokrates_bellman/src/lib.rs b/zokrates_bellman/src/lib.rs index 263cfc21..ce1ce717 100644 --- a/zokrates_bellman/src/lib.rs +++ b/zokrates_bellman/src/lib.rs @@ -11,7 +11,7 @@ use bellman::{ }; use std::collections::BTreeMap; use zokrates_ast::common::Variable; -use zokrates_ast::ir::{CanonicalLinComb, ProgIterator, Statement, Witness}; +use zokrates_ast::ir::{LinComb, ProgIterator, Statement, Witness}; use zokrates_field::BellmanFieldExtensions; use zokrates_field::Field; @@ -45,7 +45,7 @@ impl<'a, T: Field, I: IntoIterator>> Computation<'a, T, } fn bellman_combination>( - l: CanonicalLinComb, + l: LinComb, cs: &mut CS, symbols: &mut BTreeMap, witness: &mut Witness, @@ -127,19 +127,9 @@ impl<'a, T: BellmanFieldExtensions + Field, I: IntoIterator App<'static, 'static> { .help("Read arguments from stdin") .conflicts_with("arguments") .required(false) + ).arg(Arg::with_name("json") + .long("json") + .help("Write witness in a json format for debugging purposes") + .required(false) ) } @@ -195,6 +199,19 @@ fn cli_compute<'a, T: Field, I: Iterator>>( .write(writer) .map_err(|why| format!("Could not save witness: {:?}", why))?; + // write witness in the json format + if sub_matches.is_present("json") { + let json_path = Path::new(sub_matches.value_of("output").unwrap()).with_extension("json"); + let json_file = File::create(&json_path) + .map_err(|why| format!("Could not create {}: {}", json_path.display(), why))?; + + let writer = BufWriter::new(json_file); + + witness + .write_json(writer) + .map_err(|why| format!("Could not save {}: {:?}", json_path.display(), why))?; + } + // write circom witness to file let wtns_path = Path::new(sub_matches.value_of("circom-witness").unwrap()); let wtns_file = File::create(&wtns_path) diff --git a/zokrates_cli/src/ops/generate_proof.rs b/zokrates_cli/src/ops/generate_proof.rs index b2ee77d4..99857f0a 100644 --- a/zokrates_cli/src/ops/generate_proof.rs +++ b/zokrates_cli/src/ops/generate_proof.rs @@ -4,7 +4,7 @@ use rand_0_8::rngs::StdRng; use rand_0_8::SeedableRng; use std::convert::TryFrom; use std::fs::File; -use std::io::{BufReader, Read, Write}; +use std::io::{BufReader, Write}; use std::path::Path; #[cfg(feature = "ark")] use zokrates_ark::Ark; @@ -163,7 +163,9 @@ fn cli_generate_proof< let witness_file = File::open(&witness_path) .map_err(|why| format!("Could not open {}: {}", witness_path.display(), why))?; - let witness = ir::Witness::read(witness_file) + let witness_reader = BufReader::new(witness_file); + + let witness = ir::Witness::read(witness_reader) .map_err(|why| format!("Could not load witness: {:?}", why))?; let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap()); @@ -172,18 +174,14 @@ fn cli_generate_proof< let pk_file = File::open(&pk_path) .map_err(|why| format!("Could not open {}: {}", pk_path.display(), why))?; - let mut pk: Vec = Vec::new(); - let mut pk_reader = BufReader::new(pk_file); - pk_reader - .read_to_end(&mut pk) - .map_err(|why| format!("Could not read {}: {}", pk_path.display(), why))?; + let pk_reader = BufReader::new(pk_file); let mut rng = sub_matches .value_of("entropy") .map(get_rng_from_entropy) .unwrap_or_else(StdRng::from_entropy); - let proof = B::generate_proof(program, witness, pk, &mut rng); + let proof = B::generate_proof(program, witness, pk_reader, &mut rng); let mut proof_file = File::create(proof_path).unwrap(); let proof = diff --git a/zokrates_cli/src/ops/mpc/verify.rs b/zokrates_cli/src/ops/mpc/verify.rs index fa014bd0..d0520e64 100644 --- a/zokrates_cli/src/ops/mpc/verify.rs +++ b/zokrates_cli/src/ops/mpc/verify.rs @@ -73,7 +73,7 @@ fn cli_mpc_verify< let file = File::open(&path).map_err(|why| format!("Could not open `{}`: {}", path.display(), why))?; - let mut reader = BufReader::new(file); + let reader = BufReader::new(file); let radix_path = Path::new(sub_matches.value_of("radix-path").unwrap()); let radix_file = File::open(radix_path) @@ -81,7 +81,7 @@ fn cli_mpc_verify< let mut radix_reader = BufReader::new(radix_file); - let result = B::verify(&mut reader, program, &mut radix_reader) + let result = B::verify(reader, program, &mut radix_reader) .map_err(|e| format!("Verification failed: {}", e))?; let contribution_count = result.len(); diff --git a/zokrates_cli/tests/code/arithmetics.expected.witness b/zokrates_cli/tests/code/arithmetics.expected.witness deleted file mode 100644 index caaf539d..00000000 --- a/zokrates_cli/tests/code/arithmetics.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 12 diff --git a/zokrates_cli/tests/code/arithmetics.expected.witness.json b/zokrates_cli/tests/code/arithmetics.expected.witness.json new file mode 100644 index 00000000..4e1ae353 --- /dev/null +++ b/zokrates_cli/tests/code/arithmetics.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "12" +} diff --git a/zokrates_cli/tests/code/conditional_false.expected.witness b/zokrates_cli/tests/code/conditional_false.expected.witness deleted file mode 100644 index 1b8f13fa..00000000 --- a/zokrates_cli/tests/code/conditional_false.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 0 \ No newline at end of file diff --git a/zokrates_cli/tests/code/conditional_false.expected.witness.json b/zokrates_cli/tests/code/conditional_false.expected.witness.json new file mode 100644 index 00000000..50655725 --- /dev/null +++ b/zokrates_cli/tests/code/conditional_false.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "0" +} diff --git a/zokrates_cli/tests/code/conditional_true.expected.witness b/zokrates_cli/tests/code/conditional_true.expected.witness deleted file mode 100644 index 1e61044c..00000000 --- a/zokrates_cli/tests/code/conditional_true.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 1 \ No newline at end of file diff --git a/zokrates_cli/tests/code/conditional_true.expected.witness.json b/zokrates_cli/tests/code/conditional_true.expected.witness.json new file mode 100644 index 00000000..cd003d10 --- /dev/null +++ b/zokrates_cli/tests/code/conditional_true.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "1" +} diff --git a/zokrates_cli/tests/code/multidim_update.expected.witness b/zokrates_cli/tests/code/multidim_update.expected.witness deleted file mode 100644 index 0b054f2f..00000000 --- a/zokrates_cli/tests/code/multidim_update.expected.witness +++ /dev/null @@ -1,4 +0,0 @@ -~out_0 0 -~out_1 0 -~out_2 0 -~out_3 42 \ No newline at end of file diff --git a/zokrates_cli/tests/code/multidim_update.expected.witness.json b/zokrates_cli/tests/code/multidim_update.expected.witness.json new file mode 100644 index 00000000..98c0fbd7 --- /dev/null +++ b/zokrates_cli/tests/code/multidim_update.expected.witness.json @@ -0,0 +1,6 @@ +{ + "~out_0": "0", + "~out_1": "0", + "~out_2": "0", + "~out_3": "42" +} diff --git a/zokrates_cli/tests/code/n_choose_k.expected.witness b/zokrates_cli/tests/code/n_choose_k.expected.witness deleted file mode 100644 index b51f241b..00000000 --- a/zokrates_cli/tests/code/n_choose_k.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 5 \ No newline at end of file diff --git a/zokrates_cli/tests/code/n_choose_k.expected.witness.json b/zokrates_cli/tests/code/n_choose_k.expected.witness.json new file mode 100644 index 00000000..839b53c9 --- /dev/null +++ b/zokrates_cli/tests/code/n_choose_k.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "5" +} diff --git a/zokrates_cli/tests/code/no_return.expected.witness b/zokrates_cli/tests/code/no_return.expected.witness deleted file mode 100644 index e69de29b..00000000 diff --git a/zokrates_cli/tests/code/no_return.expected.witness.json b/zokrates_cli/tests/code/no_return.expected.witness.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/zokrates_cli/tests/code/no_return.expected.witness.json @@ -0,0 +1 @@ +{} diff --git a/zokrates_cli/tests/code/return_array.expected.witness b/zokrates_cli/tests/code/return_array.expected.witness deleted file mode 100644 index f9f9cf79..00000000 --- a/zokrates_cli/tests/code/return_array.expected.witness +++ /dev/null @@ -1,8 +0,0 @@ -~out_0 2 -~out_1 1 -~out_2 1 -~out_3 1 -~out_4 3 -~out_5 3 -~out_6 3 -~out_7 3 \ No newline at end of file diff --git a/zokrates_cli/tests/code/return_array.expected.witness.json b/zokrates_cli/tests/code/return_array.expected.witness.json new file mode 100644 index 00000000..d1b0a1d2 --- /dev/null +++ b/zokrates_cli/tests/code/return_array.expected.witness.json @@ -0,0 +1,10 @@ +{ + "~out_0": "2", + "~out_1": "1", + "~out_2": "1", + "~out_3": "1", + "~out_4": "3", + "~out_5": "3", + "~out_6": "3", + "~out_7": "3" +} diff --git a/zokrates_cli/tests/code/simple_add.expected.witness b/zokrates_cli/tests/code/simple_add.expected.witness deleted file mode 100644 index 23b7f950..00000000 --- a/zokrates_cli/tests/code/simple_add.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 3 diff --git a/zokrates_cli/tests/code/simple_add.expected.witness.json b/zokrates_cli/tests/code/simple_add.expected.witness.json new file mode 100644 index 00000000..ca9fd972 --- /dev/null +++ b/zokrates_cli/tests/code/simple_add.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "3" +} diff --git a/zokrates_cli/tests/code/simple_mul.expected.witness b/zokrates_cli/tests/code/simple_mul.expected.witness deleted file mode 100644 index 8eb3a8d7..00000000 --- a/zokrates_cli/tests/code/simple_mul.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 24 \ No newline at end of file diff --git a/zokrates_cli/tests/code/simple_mul.expected.witness.json b/zokrates_cli/tests/code/simple_mul.expected.witness.json new file mode 100644 index 00000000..7143416e --- /dev/null +++ b/zokrates_cli/tests/code/simple_mul.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "24" +} diff --git a/zokrates_cli/tests/code/taxation.expected.witness b/zokrates_cli/tests/code/taxation.expected.witness deleted file mode 100644 index 1b8f13fa..00000000 --- a/zokrates_cli/tests/code/taxation.expected.witness +++ /dev/null @@ -1 +0,0 @@ -~out_0 0 \ No newline at end of file diff --git a/zokrates_cli/tests/code/taxation.expected.witness.json b/zokrates_cli/tests/code/taxation.expected.witness.json new file mode 100644 index 00000000..50655725 --- /dev/null +++ b/zokrates_cli/tests/code/taxation.expected.witness.json @@ -0,0 +1,3 @@ +{ + "~out_0": "0" +} diff --git a/zokrates_cli/tests/integration.rs b/zokrates_cli/tests/integration.rs index 35aff1de..cd988b10 100644 --- a/zokrates_cli/tests/integration.rs +++ b/zokrates_cli/tests/integration.rs @@ -16,17 +16,78 @@ mod integration { use std::fs; use std::fs::File; use std::io::{BufReader, Read, Write}; - use std::panic; use std::path::Path; use std::process::Command; use tempdir::TempDir; use zokrates_abi::{parse_strict, Encode}; + use zokrates_ast::ir::Witness; use zokrates_ast::typed::abi::Abi; use zokrates_field::Bn128Field; use zokrates_proof_systems::{ to_token::ToToken, Marlin, Proof, SolidityCompatibleScheme, G16, GM17, }; + mod helpers { + use super::*; + use zokrates_ast::common::Variable; + use zokrates_field::Field; + + pub fn parse_variable(s: &str) -> Result { + if s == "~one" { + return Ok(Variable::one()); + } + + let mut public = s.split("~out_"); + match public.nth(1) { + Some(v) => { + let v = v.parse().map_err(|_| s)?; + Ok(Variable::public(v)) + } + None => { + let mut private = s.split('_'); + match private.nth(1) { + Some(v) => { + let v = v.parse().map_err(|_| s)?; + Ok(Variable::new(v)) + } + None => Err(s), + } + } + } + } + + pub fn parse_witness_json(reader: R) -> std::io::Result> { + use std::io::{Error, ErrorKind}; + + let json: serde_json::Value = serde_json::from_reader(reader)?; + let object = json + .as_object() + .ok_or_else(|| Error::new(ErrorKind::Other, "Witness must be an object"))?; + + let mut witness = Witness::empty(); + for (k, v) in object { + let variable = parse_variable(k).map_err(|why| { + Error::new( + ErrorKind::Other, + format!("Invalid variable in witness: {}", why), + ) + })?; + + let value = v + .as_str() + .ok_or_else(|| Error::new(ErrorKind::Other, "Witness value must be a string")) + .and_then(|v| { + T::try_from_dec_str(v).map_err(|_| { + Error::new(ErrorKind::Other, format!("Invalid value in witness: {}", v)) + }) + })?; + + witness.insert(variable, value); + } + Ok(witness) + } + } + macro_rules! map( { $($key:expr => $value:expr),+ } => { @@ -101,7 +162,9 @@ mod integration { let program_name = Path::new(Path::new(path.file_stem().unwrap()).file_stem().unwrap()); let prog = dir.join(program_name).with_extension("zok"); - let witness = dir.join(program_name).with_extension("expected.witness"); + let witness = dir + .join(program_name) + .with_extension("expected.witness.json"); let json_input = dir.join(program_name).with_extension("arguments.json"); test_compile_and_witness( @@ -250,33 +313,24 @@ mod integration { .unwrap(); // load the expected witness - let mut expected_witness_file = File::open(&expected_witness_path).unwrap(); - let mut expected_witness = String::new(); - expected_witness_file - .read_to_string(&mut expected_witness) - .unwrap(); + let expected_witness_file = File::open(&expected_witness_path).unwrap(); + let expected_witness: Witness = + helpers::parse_witness_json(expected_witness_file).unwrap(); // load the actual witness - let mut witness_file = File::open(&witness_path).unwrap(); - let mut witness = String::new(); - witness_file.read_to_string(&mut witness).unwrap(); + let witness_file = File::open(&witness_path).unwrap(); + let witness = Witness::::read(witness_file).unwrap(); // load the actual inline witness - let mut inline_witness_file = File::open(&inline_witness_path).unwrap(); - let mut inline_witness = String::new(); - inline_witness_file - .read_to_string(&mut inline_witness) - .unwrap(); + let inline_witness_file = File::open(&inline_witness_path).unwrap(); + let inline_witness = + Witness::::read(inline_witness_file).unwrap(); assert_eq!(inline_witness, witness); - for line in expected_witness.as_str().split('\n') { - assert!( - witness.contains(line), - "Witness generation failed for {}\n\nLine \"{}\" not found in witness", - program_path.to_str().unwrap(), - line - ); + for (k, v) in expected_witness.0 { + let value = witness.0.get(&k).expect("should contain key"); + assert!(v.eq(value)); } let backends = map! { diff --git a/zokrates_field/Cargo.toml b/zokrates_field/Cargo.toml index bb420c7f..8bcde430 100644 --- a/zokrates_field/Cargo.toml +++ b/zokrates_field/Cargo.toml @@ -29,7 +29,7 @@ ark-bn254 = { version = "^0.3.0", features = ["curve"], default-features = false ark-bls12-377 = { version = "^0.3.0", features = ["curve"], default-features = false } ark-bls12-381 = { version = "^0.3.0", features = ["curve"] } ark-bw6-761 = { version = "^0.3.0", default-features = false } -ark-serialize = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = ["std"] } [dev-dependencies] rand = "0.4" diff --git a/zokrates_field/src/dummy_curve.rs b/zokrates_field/src/dummy_curve.rs index b1326592..55460f03 100644 --- a/zokrates_field/src/dummy_curve.rs +++ b/zokrates_field/src/dummy_curve.rs @@ -252,4 +252,12 @@ impl Field for FieldPrime { fn to_biguint(&self) -> num_bigint::BigUint { unimplemented!() } + + fn read(_: R) -> std::io::Result { + unimplemented!() + } + + fn write(&self, _: W) -> std::io::Result<()> { + unimplemented!() + } } diff --git a/zokrates_field/src/lib.rs b/zokrates_field/src/lib.rs index 81e9915f..a767f7d0 100644 --- a/zokrates_field/src/lib.rs +++ b/zokrates_field/src/lib.rs @@ -8,7 +8,6 @@ extern crate num_bigint; #[cfg(feature = "bellman")] use bellman_ce::pairing::{ff::ScalarEngine, Engine}; - use num_bigint::BigUint; use num_traits::{CheckedDiv, One, Zero}; use serde::{Deserialize, Serialize}; @@ -16,6 +15,7 @@ use std::convert::{From, TryFrom}; use std::fmt; use std::fmt::{Debug, Display}; use std::hash::Hash; +use std::io::{Read, Write}; use std::ops::{Add, Div, Mul, Sub}; pub trait Pow { @@ -96,6 +96,10 @@ pub trait Field: + num_traits::CheckedMul { const G2_TYPE: G2Type = G2Type::Fq2; + // Read field from the reader + fn read(reader: R) -> std::io::Result; + // Write field to the writer + fn write(&self, writer: W) -> std::io::Result<()>; /// Returns this `Field`'s contents as little-endian byte vector fn to_byte_vector(&self) -> Vec; /// Returns an element of this `Field` from a little-endian byte vector @@ -145,6 +149,7 @@ mod prime_field { use std::convert::TryFrom; use std::fmt; use std::fmt::{Debug, Display}; + use std::io::{Read, Write}; use std::ops::{Add, Div, Mul, Sub}; type Fr = <$v as ark_ec::PairingEngine>::Fr; @@ -187,9 +192,21 @@ mod prime_field { self.v.into_repr().to_bytes_le() } + fn read(reader: R) -> std::io::Result { + use ark_ff::FromBytes; + Ok(FieldPrime { + v: Fr::read(reader)?, + }) + } + + fn write(&self, mut writer: W) -> std::io::Result<()> { + use ark_ff::ToBytes; + self.v.write(&mut writer)?; + Ok(()) + } + fn from_byte_vector(bytes: Vec) -> Self { use ark_ff::FromBytes; - FieldPrime { v: Fr::from(::BigInt::read(&bytes[..]).unwrap()), } @@ -592,9 +609,12 @@ mod prime_field { } fn into_bellman(self) -> ::Fr { - use bellman_ce::pairing::ff::PrimeField; - let s = self.to_dec_string(); - ::Fr::from_str(&s).unwrap() + use bellman_ce::pairing::ff::{PrimeField, PrimeFieldRepr}; + let bytes = self.to_byte_vector(); + let mut repr = + <::Fr as PrimeField>::Repr::default(); + repr.read_le(bytes.as_slice()).unwrap(); + ::Fr::from_repr(repr).unwrap() } fn new_fq2( diff --git a/zokrates_js/index.d.ts b/zokrates_js/index.d.ts index d2755a2c..ff7cba24 100644 --- a/zokrates_js/index.d.ts +++ b/zokrates_js/index.d.ts @@ -42,7 +42,7 @@ declare module "zokrates-js" { } export interface ComputationResult { - witness: string; + witness: Uint8Array; output: string; snarkjs?: { witness: Uint8Array; @@ -90,7 +90,7 @@ declare module "zokrates-js" { setupWithSrs(srs: Uint8Array, program: Uint8Array): SetupKeypair; generateProof( program: Uint8Array, - witness: string, + witness: Uint8Array, provingKey: Uint8Array, entropy?: string ): Proof; diff --git a/zokrates_js/src/lib.rs b/zokrates_js/src/lib.rs index 668bc497..ed3f2650 100644 --- a/zokrates_js/src/lib.rs +++ b/zokrates_js/src/lib.rs @@ -79,15 +79,17 @@ pub struct ResolverResult { #[wasm_bindgen] pub struct ComputationResult { - witness: String, + witness: Vec, output: String, snarkjs_witness: Option>, } #[wasm_bindgen] impl ComputationResult { - pub fn witness(&self) -> JsValue { - JsValue::from_str(&self.witness) + pub fn witness(&self) -> js_sys::Uint8Array { + let arr = js_sys::Uint8Array::new_with_length(self.witness.len() as u32); + arr.copy_from(&self.witness); + arr } pub fn output(&self) -> JsValue { @@ -360,8 +362,14 @@ mod internal { buffer.into_inner() }); + let witness = { + let mut buffer = Cursor::new(vec![]); + witness.write(&mut buffer).unwrap(); + buffer.into_inner() + }; + Ok(ComputationResult { - witness: format!("{}", witness), + witness, output: to_string_pretty(&return_values).unwrap(), snarkjs_witness, }) @@ -416,15 +424,14 @@ mod internal { pub fn generate_proof, B: Backend, R: RngCore + CryptoRng>( prog: ir::Prog, - witness: JsValue, + witness: &[u8], pk: &[u8], rng: &mut R, ) -> Result { - let str_witness = witness.as_string().unwrap(); - let ir_witness: ir::Witness = ir::Witness::read(str_witness.as_bytes()) + let ir_witness: ir::Witness = ir::Witness::read(witness) .map_err(|err| JsValue::from_str(&format!("Could not read witness: {}", err)))?; - let proof = B::generate_proof(prog, ir_witness, pk.to_vec(), rng); + let proof = B::generate_proof(prog, ir_witness, pk, rng); Ok(JsValue::from_serde(&TaggedProof::::new(proof.proof, proof.inputs)).unwrap()) } @@ -696,7 +703,7 @@ pub fn universal_setup(curve: JsValue, size: u32, entropy: JsValue) -> Result> { - fn generate_proof<'a, I: IntoIterator>, R: RngCore + CryptoRng>( + fn generate_proof< + 'a, + I: IntoIterator>, + R: Read, + G: RngCore + CryptoRng, + >( program: ir::ProgIterator<'a, T, I>, witness: ir::Witness, - proving_key: Vec, - rng: &mut R, + proving_key: R, + rng: &mut G, ) -> Proof; fn verify(vk: S::VerificationKey, proof: Proof) -> bool; @@ -129,16 +134,16 @@ pub trait MpcBackend> { ) -> Result<(), String>; fn contribute( - params: &mut R, + params: R, rng: &mut G, output: &mut W, ) -> Result<[u8; 64], String>; - fn verify<'a, P: Read, R: Read, I: IntoIterator>>( - params: &mut P, + fn verify<'a, R: Read, I: IntoIterator>>( + params: R, program: ir::ProgIterator<'a, T, I>, phase1_radix: &mut R, ) -> Result, String>; - fn export_keypair(params: &mut R) -> Result, String>; + fn export_keypair(params: R) -> Result, String>; } diff --git a/zokrates_test/tests/wasm.rs b/zokrates_test/tests/wasm.rs index 659f5a23..cd9fc57a 100644 --- a/zokrates_test/tests/wasm.rs +++ b/zokrates_test/tests/wasm.rs @@ -30,6 +30,10 @@ fn generate_proof() { let rng = &mut StdRng::from_entropy(); let keypair = >::setup(program.clone(), rng); - let _proof = - >::generate_proof(program, witness, keypair.pk, rng); + let _proof = >::generate_proof( + program, + witness, + keypair.pk.as_slice(), + rng, + ); }