1
0
Fork 0
mirror of synced 2025-09-23 04:08:33 +00:00

Merge pull request #1289 from Zokrates/backend-opt

Optimize backend integrations
This commit is contained in:
Thibaut Schaeffer 2023-04-11 22:58:55 +02:00 committed by GitHub
commit 10ebd604f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 379 additions and 257 deletions

1
.gitignore vendored
View file

@ -13,6 +13,7 @@ verifier.sol
proof.json
universal_setup.dat
witness
witness.json
# ZoKrates source files at the root of the repository
/*.zok

9
Cargo.lock generated
View file

@ -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",

View file

@ -0,0 +1 @@
Change witness format to binary, optimize backend integration code to improve proving time

View file

@ -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 }

View file

@ -40,11 +40,16 @@ impl<T: Field + ArkFieldExtensions> NonUniversalBackend<T, GM17> for Ark {
}
impl<T: Field + ArkFieldExtensions> Backend<T, GM17> for Ark {
fn generate_proof<'a, I: IntoIterator<Item = Statement<'a, T>>, R: RngCore + CryptoRng>(
fn generate_proof<
'a,
I: IntoIterator<Item = Statement<'a, T>>,
R: std::io::Read,
G: RngCore + CryptoRng,
>(
program: ProgIterator<'a, T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
rng: &mut R,
proving_key: R,
rng: &mut G,
) -> Proof<T, GM17> {
let computation = Computation::with_witness(program, witness);
@ -54,10 +59,9 @@ impl<T: Field + ArkFieldExtensions> Backend<T, GM17> for Ark {
.map(parse_fr::<T>)
.collect::<Vec<_>>();
let pk = ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_unchecked(
&mut proving_key.as_slice(),
)
.unwrap();
let pk =
ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_unchecked(proving_key)
.unwrap();
let proof = ArkGM17::<T::ArkEngine>::prove(&pk, computation, rng).unwrap();
let proof_points = ProofPoints {
@ -136,7 +140,10 @@ mod tests {
.unwrap();
let proof = <Ark as Backend<Bls12_377Field, GM17>>::generate_proof(
program, witness, keypair.pk, rng,
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bls12_377Field, GM17>>::verify(keypair.vk, proof);
@ -160,8 +167,12 @@ mod tests {
.execute(program.clone(), &[Bw6_761Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bw6_761Field, GM17>>::generate_proof(program, witness, keypair.pk, rng);
let proof = <Ark as Backend<Bw6_761Field, GM17>>::generate_proof(
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bw6_761Field, GM17>>::verify(keypair.vk, proof);
assert!(ans);

View file

@ -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<T: Field + ArkFieldExtensions> Backend<T, G16> for Ark {
fn generate_proof<'a, I: IntoIterator<Item = Statement<'a, T>>, R: RngCore + CryptoRng>(
fn generate_proof<
'a,
I: IntoIterator<Item = Statement<'a, T>>,
R: Read,
G: RngCore + CryptoRng,
>(
program: ProgIterator<'a, T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
rng: &mut R,
proving_key: R,
rng: &mut G,
) -> Proof<T, G16> {
let computation = Computation::with_witness(program, witness);
@ -31,12 +37,12 @@ impl<T: Field + ArkFieldExtensions> Backend<T, G16> for Ark {
.map(parse_fr::<T>)
.collect::<Vec<_>>();
let pk = ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_unchecked(
&mut proving_key.as_slice(),
)
.unwrap();
let pk =
ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_unchecked(proving_key)
.unwrap();
let proof = Groth16::<T::ArkEngine>::prove(&pk, computation, rng).unwrap();
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
@ -133,7 +139,10 @@ mod tests {
.unwrap();
let proof = <Ark as Backend<Bls12_377Field, G16>>::generate_proof(
program, witness, keypair.pk, rng,
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bls12_377Field, G16>>::verify(keypair.vk, proof);
@ -157,8 +166,12 @@ mod tests {
.execute(program.clone(), &[Bw6_761Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bw6_761Field, G16>>::generate_proof(program, witness, keypair.pk, rng);
let proof = <Ark as Backend<Bw6_761Field, G16>>::generate_proof(
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bw6_761Field, G16>>::verify(keypair.vk, proof);
assert!(ans);

View file

@ -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<Item = Statement<'a, T>>> Computation<'a, T, I> {
}
fn ark_combination<T: Field + ArkFieldExtensions>(
l: CanonicalLinComb<T>,
l: LinComb<T>,
cs: &mut ConstraintSystem<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
symbols: &mut BTreeMap<Variable, ArkVariable>,
witness: &mut Witness<T>,
@ -113,24 +113,9 @@ impl<'a, T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<'a, T>>
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)?;
}

View file

@ -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<T: Field + ArkFieldExtensions> UniversalBackend<T, marlin::Marlin> for Ark
}
impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
fn generate_proof<'a, I: IntoIterator<Item = Statement<'a, T>>, R: RngCore + CryptoRng>(
fn generate_proof<
'a,
I: IntoIterator<Item = Statement<'a, T>>,
R: Read,
G: RngCore + CryptoRng,
>(
program: ProgIterator<'a, T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
rng: &mut R,
proving_key: R,
rng: &mut G,
) -> Proof<T, marlin::Marlin> {
let computation = Computation::with_witness(program, witness);
@ -220,7 +226,7 @@ impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
T::ArkEngine,
DensePolynomial<<<T as ArkFieldExtensions>::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 = <Ark as Backend<Bls12_377Field, Marlin>>::generate_proof(
program, witness, keypair.pk, rng,
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bls12_377Field, Marlin>>::verify(keypair.vk, proof);
@ -454,7 +463,10 @@ mod tests {
.unwrap();
let proof = <Ark as Backend<Bw6_761Field, Marlin>>::generate_proof(
program, witness, keypair.pk, rng,
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Ark as Backend<Bw6_761Field, Marlin>>::verify(keypair.vk, proof);

View file

@ -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"] }

View file

@ -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;
}
}

View file

@ -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<Self, &str> {
if s == "~one" {
return Ok(Variable::one());
}
pub fn write<W: 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<R: Read>(mut reader: R) -> std::io::Result<Self> {
let mut buf = [0; std::mem::size_of::<isize>()];
reader.read_exact(&mut buf)?;
Ok(Variable {
id: isize::from_le_bytes(buf),
})
}
}

View file

@ -41,54 +41,44 @@ impl<T: Field> Witness<T> {
Witness(BTreeMap::new())
}
pub fn write<W: 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<W: 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<R: Read>(mut reader: R) -> io::Result<Self> {
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::<io::Result<BTreeMap<Variable, T>>>()?;
let mut buf = [0; std::mem::size_of::<usize>()];
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<W: Write>(&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::Map<String, serde_json::Value>>();
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::<Bn128Field>::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::<Bn128Field>::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::<Bn128Field>::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"
}"#
)
}
}
}

View file

@ -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 }

View file

@ -20,14 +20,19 @@ use zokrates_proof_systems::groth16::{ProofPoints, VerificationKey, G16};
use zokrates_proof_systems::Scheme;
impl<T: Field + BellmanFieldExtensions> Backend<T, G16> for Bellman {
fn generate_proof<'a, I: IntoIterator<Item = Statement<'a, T>>, R: RngCore + CryptoRng>(
fn generate_proof<
'a,
I: IntoIterator<Item = Statement<'a, T>>,
R: Read,
G: RngCore + CryptoRng,
>(
program: ProgIterator<'a, T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
rng: &mut R,
proving_key: R,
rng: &mut G,
) -> Proof<T, G16> {
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<String> = computation
.public_inputs_values()
@ -108,7 +113,7 @@ impl<T: Field + BellmanFieldExtensions> MpcBackend<T, G16> for Bellman {
}
fn contribute<R: Read, W: Write, G: RngCore + CryptoRng>(
params: &mut R,
params: R,
rng: &mut G,
output: &mut W,
) -> Result<[u8; 64], String> {
@ -124,8 +129,8 @@ impl<T: Field + BellmanFieldExtensions> MpcBackend<T, G16> for Bellman {
Ok(hash)
}
fn verify<'a, P: Read, R: Read, I: IntoIterator<Item = Statement<'a, T>>>(
params: &mut P,
fn verify<'a, R: Read, I: IntoIterator<Item = Statement<'a, T>>>(
params: R,
program: ProgIterator<'a, T, I>,
phase1_radix: &mut R,
) -> Result<Vec<[u8; 64]>, String> {
@ -140,7 +145,7 @@ impl<T: Field + BellmanFieldExtensions> MpcBackend<T, G16> for Bellman {
Ok(hashes)
}
fn export_keypair<R: Read>(params: &mut R) -> Result<SetupKeypair<T, G16>, String> {
fn export_keypair<R: Read>(params: R) -> Result<SetupKeypair<T, G16>, String> {
let params =
MPCParameters::<T::BellmanEngine>::read(params, true).map_err(|e| e.to_string())?;
@ -227,7 +232,10 @@ mod tests {
.unwrap();
let proof = <Bellman as Backend<Bn128Field, G16>>::generate_proof(
program, witness, keypair.pk, rng,
program,
witness,
keypair.pk.as_slice(),
rng,
);
let ans = <Bellman as Backend<Bn128Field, G16>>::verify(keypair.vk, proof);

View file

@ -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<Item = Statement<'a, T>>> Computation<'a, T,
}
fn bellman_combination<T: BellmanFieldExtensions, CS: ConstraintSystem<T::BellmanEngine>>(
l: CanonicalLinComb<T>,
l: LinComb<T>,
cs: &mut CS,
symbols: &mut BTreeMap<Variable, BellmanVariable>,
witness: &mut Witness<T>,
@ -127,19 +127,9 @@ impl<'a, T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<'a,
for statement in self.program.statements {
if let Statement::Constraint(quad, lin, _) = statement {
let a = &bellman_combination(
quad.left.into_canonical(),
cs,
&mut symbols,
&mut witness,
);
let b = &bellman_combination(
quad.right.into_canonical(),
cs,
&mut symbols,
&mut witness,
);
let c = &bellman_combination(lin.into_canonical(), cs, &mut symbols, &mut witness);
let a = &bellman_combination(quad.left, cs, &mut symbols, &mut witness);
let b = &bellman_combination(quad.right, cs, &mut symbols, &mut witness);
let c = &bellman_combination(lin, cs, &mut symbols, &mut witness);
cs.enforce(|| "Constraint", |lc| lc + a, |lc| lc + b, |lc| lc + c);
}

View file

@ -43,20 +43,14 @@ As a next step we can create a witness file using the following command:
Using the flag `-a` we pass arguments to the program. Recall that our goal is to compute the hash for the number `5`. Consequently we set `a`, `b` and `c` to `0` and `d` to `5`.
Still here? Great! At this point, we can check the `witness` file for the return values:
Still here? Great! At this point we can check the return values. We should see the following output:
```
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:13}}
Witness:
["263561599766550617289250058199814760685","65303172752238645975888084098459749904"]
```
which should lead to the following output:
```sh
~out_0 263561599766550617289250058199814760685
~out_1 65303172752238645975888084098459749904
```
Hence, by concatenating the outputs as 128 bit numbers, we arrive at the following value as the hash for our selected pre-image :
By concatenating the outputs as 128 bit numbers, we arrive at the following value as the hash for our selected pre-image :
`0xc6481e22c5ff4164af680b8cfaa5e8ed3120eeff89c4f307c4a6faaae059ce10`
## Prove knowledge of pre-image
@ -78,13 +72,13 @@ Note that we now compare the result of `sha256packed` with the hard-coded correc
So, having defined the program, Victor is now ready to compile the code:
```
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:17}}
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:15}}
```
Based on that Victor can run the setup phase and export a verifier smart contract as a Solidity file:
```
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:18:19}}
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:16:17}}
```
`setup` creates a `verification.key` file and a `proving.key` file. Victor gives the proving key to Peggy.
@ -94,13 +88,13 @@ Based on that Victor can run the setup phase and export a verifier smart contrac
Peggy provides the correct pre-image as an argument to the program.
```
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:20}}
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:18}}
```
Finally, Peggy can run the command to construct the proof:
```
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:21}}
{{#include ../../../zokrates_cli/examples/book/sha256_tutorial/test.sh:19}}
```
As the inputs were declared as private in the program, they do not appear in the proof thanks to the zero-knowledge property of the protocol.

View file

@ -8,9 +8,7 @@ function zokrates() {
}
zokrates compile -i hashexample.zok
zokrates compute-witness -a 0 0 0 5
grep '~out' witness
zokrates compute-witness -a 0 0 0 5 --verbose
cp -f hashexample_updated.zok hashexample.zok

View file

@ -66,6 +66,10 @@ pub fn subcommand() -> 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<Item = ir::Statement<'a, T>>>(
.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)

View file

@ -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<u8> = 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 =

View file

@ -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();

View file

@ -1 +0,0 @@
~out_0 12

View file

@ -0,0 +1,3 @@
{
"~out_0": "12"
}

View file

@ -0,0 +1,3 @@
{
"~out_0": "0"
}

View file

@ -0,0 +1,3 @@
{
"~out_0": "1"
}

View file

@ -1,4 +0,0 @@
~out_0 0
~out_1 0
~out_2 0
~out_3 42

View file

@ -0,0 +1,6 @@
{
"~out_0": "0",
"~out_1": "0",
"~out_2": "0",
"~out_3": "42"
}

View file

@ -1 +0,0 @@
~out_0 5

View file

@ -0,0 +1,3 @@
{
"~out_0": "5"
}

View file

@ -0,0 +1 @@
{}

View file

@ -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

View file

@ -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"
}

View file

@ -1 +0,0 @@
~out_0 3

View file

@ -0,0 +1,3 @@
{
"~out_0": "3"
}

View file

@ -1 +0,0 @@
~out_0 24

View file

@ -0,0 +1,3 @@
{
"~out_0": "24"
}

View file

@ -1 +0,0 @@
~out_0 0

View file

@ -0,0 +1,3 @@
{
"~out_0": "0"
}

View file

@ -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<Variable, &str> {
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<T: Field, R: Read>(reader: R) -> std::io::Result<Witness<T>> {
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<zokrates_field::Bn128Field> =
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::<zokrates_field::Bn128Field>::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::<zokrates_field::Bn128Field>::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! {

View file

@ -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"

View file

@ -252,4 +252,12 @@ impl Field for FieldPrime {
fn to_biguint(&self) -> num_bigint::BigUint {
unimplemented!()
}
fn read<R: std::io::Read>(_: R) -> std::io::Result<Self> {
unimplemented!()
}
fn write<W: std::io::Write>(&self, _: W) -> std::io::Result<()> {
unimplemented!()
}
}

View file

@ -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<RHS> {
@ -96,6 +96,10 @@ pub trait Field:
+ num_traits::CheckedMul
{
const G2_TYPE: G2Type = G2Type::Fq2;
// Read field from the reader
fn read<R: Read>(reader: R) -> std::io::Result<Self>;
// Write field to the writer
fn write<W: Write>(&self, writer: W) -> std::io::Result<()>;
/// Returns this `Field`'s contents as little-endian byte vector
fn to_byte_vector(&self) -> Vec<u8>;
/// 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<R: Read>(reader: R) -> std::io::Result<Self> {
use ark_ff::FromBytes;
Ok(FieldPrime {
v: Fr::read(reader)?,
})
}
fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
use ark_ff::ToBytes;
self.v.write(&mut writer)?;
Ok(())
}
fn from_byte_vector(bytes: Vec<u8>) -> Self {
use ark_ff::FromBytes;
FieldPrime {
v: Fr::from(<Fr as PrimeField>::BigInt::read(&bytes[..]).unwrap()),
}
@ -592,9 +609,12 @@ mod prime_field {
}
fn into_bellman(self) -> <Self::BellmanEngine as ScalarEngine>::Fr {
use bellman_ce::pairing::ff::PrimeField;
let s = self.to_dec_string();
<Self::BellmanEngine as ScalarEngine>::Fr::from_str(&s).unwrap()
use bellman_ce::pairing::ff::{PrimeField, PrimeFieldRepr};
let bytes = self.to_byte_vector();
let mut repr =
<<Self::BellmanEngine as ScalarEngine>::Fr as PrimeField>::Repr::default();
repr.read_le(bytes.as_slice()).unwrap();
<Self::BellmanEngine as ScalarEngine>::Fr::from_repr(repr).unwrap()
}
fn new_fq2(

View file

@ -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;

View file

@ -79,15 +79,17 @@ pub struct ResolverResult {
#[wasm_bindgen]
pub struct ComputationResult {
witness: String,
witness: Vec<u8>,
output: String,
snarkjs_witness: Option<Vec<u8>>,
}
#[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<T: Field, S: Scheme<T>, B: Backend<T, S>, R: RngCore + CryptoRng>(
prog: ir::Prog<T>,
witness: JsValue,
witness: &[u8],
pk: &[u8],
rng: &mut R,
) -> Result<JsValue, JsValue> {
let str_witness = witness.as_string().unwrap();
let ir_witness: ir::Witness<T> = ir::Witness::read(str_witness.as_bytes())
let ir_witness: ir::Witness<T> = 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::<T, S>::new(proof.proof, proof.inputs)).unwrap())
}
@ -696,7 +703,7 @@ pub fn universal_setup(curve: JsValue, size: u32, entropy: JsValue) -> Result<Ve
#[wasm_bindgen]
pub fn generate_proof(
program: &[u8],
witness: JsValue,
witness: &[u8],
pk: &[u8],
entropy: JsValue,
options: JsValue,

View file

@ -96,11 +96,16 @@ impl ToString for G2AffineFq2 {
}
pub trait Backend<T: Field, S: Scheme<T>> {
fn generate_proof<'a, I: IntoIterator<Item = ir::Statement<'a, T>>, R: RngCore + CryptoRng>(
fn generate_proof<
'a,
I: IntoIterator<Item = ir::Statement<'a, T>>,
R: Read,
G: RngCore + CryptoRng,
>(
program: ir::ProgIterator<'a, T, I>,
witness: ir::Witness<T>,
proving_key: Vec<u8>,
rng: &mut R,
proving_key: R,
rng: &mut G,
) -> Proof<T, S>;
fn verify(vk: S::VerificationKey, proof: Proof<T, S>) -> bool;
@ -129,16 +134,16 @@ pub trait MpcBackend<T: Field, S: Scheme<T>> {
) -> Result<(), String>;
fn contribute<R: Read, W: Write, G: RngCore + CryptoRng>(
params: &mut R,
params: R,
rng: &mut G,
output: &mut W,
) -> Result<[u8; 64], String>;
fn verify<'a, P: Read, R: Read, I: IntoIterator<Item = ir::Statement<'a, T>>>(
params: &mut P,
fn verify<'a, R: Read, I: IntoIterator<Item = ir::Statement<'a, T>>>(
params: R,
program: ir::ProgIterator<'a, T, I>,
phase1_radix: &mut R,
) -> Result<Vec<[u8; 64]>, String>;
fn export_keypair<R: Read>(params: &mut R) -> Result<SetupKeypair<T, S>, String>;
fn export_keypair<R: Read>(params: R) -> Result<SetupKeypair<T, S>, String>;
}

View file

@ -30,6 +30,10 @@ fn generate_proof() {
let rng = &mut StdRng::from_entropy();
let keypair = <Ark as NonUniversalBackend<Bn128Field, G16>>::setup(program.clone(), rng);
let _proof =
<Ark as Backend<Bn128Field, G16>>::generate_proof(program, witness, keypair.pk, rng);
let _proof = <Ark as Backend<Bn128Field, G16>>::generate_proof(
program,
witness,
keypair.pk.as_slice(),
rng,
);
}