Merge pull request #273 from Zokrates/bellman-backend-v2
Add support for Groth16 with bellman
This commit is contained in:
commit
2305cf0684
42 changed files with 1941 additions and 1134 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@ proving.key
|
|||
variables.inf
|
||||
verification.key
|
||||
verifier.sol
|
||||
proof.json
|
||||
witness
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
|
|
563
Cargo.lock
generated
563
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,6 @@ MAINTAINER JacobEberhardt <jacob.eberhardt@tu-berlin.de>, Thibaut Schaeffer <thi
|
|||
|
||||
RUN useradd -u 1000 -m zokrates
|
||||
|
||||
ARG RUST_TOOLCHAIN=nightly-2019-01-01
|
||||
ENV WITH_LIBSNARK=1
|
||||
ENV ZOKRATES_HOME=/home/zokrates/ZoKrates/zokrates_stdlib/stdlib/
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
- [Reference](reference/index.md)
|
||||
- [CLI](reference/cli.md)
|
||||
- [Backends](reference/backends.md)
|
||||
- [Proving schemes](reference/proving_schemes.md)
|
||||
- [Verification](reference/verification.md)
|
||||
- [ZIR](reference/ir.md)
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# Backends
|
||||
|
||||
ZoKrates supports different proof systems to be used as backends. All of the available backends rely on the ALT_BN128 curve, which means that they're all compatible with Ethereum.
|
||||
|
||||
We identify the backends by the reference to the paper that introduced them. Currently the options available are:
|
||||
|
||||
| Name | Paper | CLI flag |
|
||||
| ---- | ----- | -------- |
|
||||
| PGHR13 | [Here](https://eprint.iacr.org/2013/279) | `--backend pghr13` |
|
||||
| GM17 | [Here](https://eprint.iacr.org/2017/540) | `--backend gm17` |
|
||||
|
||||
The default backend is PGHR13.
|
||||
|
||||
When not using the default, the CLI flag has to be provided for the following commands:
|
||||
- `setup`
|
||||
- `export-verifier`
|
||||
- `generate-proof`
|
|
@ -4,6 +4,6 @@ The reference covers the details of various areas of ZoKrates.
|
|||
|
||||
- [ZoKrates Reference](index.md)
|
||||
- [CLI](cli.md)
|
||||
- [Backends](backends.md)
|
||||
- [Proving schemes](proving_schemes.md)
|
||||
- [Verification](verification.md)
|
||||
- [ZIR](ir.md)
|
||||
|
|
26
zokrates_book/src/reference/proving_schemes.md
Normal file
26
zokrates_book/src/reference/proving_schemes.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Proving schemes
|
||||
|
||||
ZoKrates supports different proving schemes. All of the available schemes rely on the ALT_BN128 curve, which means that they're all compatible with Ethereum.
|
||||
|
||||
We identify the schemes by the reference to the paper that introduced them. Currently the options available are:
|
||||
|
||||
| Name | Paper | CLI flag | Requires libsnark |
|
||||
| ---- | ----- | -------- | --------- |
|
||||
| PGHR13 | [Here](https://eprint.iacr.org/2013/279) | `--proving-scheme pghr13` | Yes |
|
||||
| G16 | [Here](https://eprint.iacr.org/2016/260) | `--proving-scheme g16` | No |
|
||||
| GM17 | [Here](https://eprint.iacr.org/2017/540) | `--proving-scheme gm17` | Yes |
|
||||
|
||||
The default proving scheme is G16.
|
||||
|
||||
When not using the default, the CLI flag has to be provided for the following commands:
|
||||
- `setup`
|
||||
- `export-verifier`
|
||||
- `generate-proof`
|
||||
|
||||
## G16 malleability
|
||||
|
||||
When using G16, developers should pay attention to the fact that an attacker seeing a valid proof can very easily generate a different but still valid proof. Therefore, depending on the use case, making sure on chain that the same proof cannot be submitted twice may *not* be enough to guarantee that attackers cannot replay proofs. Mechanisms to solve this issue include:
|
||||
- signed proofs
|
||||
- nullifiers
|
||||
- usage of an ethereum address as a public input to the program
|
||||
- usage of non-malleable schemes such as GM17
|
|
@ -16,11 +16,12 @@ regex = "0.2"
|
|||
zokrates_field = { version = "0.3", path = "../zokrates_field" }
|
||||
zokrates_core = { version = "0.3", path = "../zokrates_core" }
|
||||
zokrates_fs_resolver = { version = "0.4", path = "../zokrates_fs_resolver"}
|
||||
serde_json = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
glob = "0.2.11"
|
||||
assert_cli = "0.5"
|
||||
serde_json = "1.0"
|
||||
tempdir = "0.3"
|
||||
|
||||
[[bin]]
|
||||
name = "zokrates"
|
||||
|
|
|
@ -6,21 +6,14 @@
|
|||
|
||||
use bincode::{deserialize_from, serialize_into, Infinite};
|
||||
use clap::{App, AppSettings, Arg, SubCommand};
|
||||
#[cfg(feature = "libsnark")]
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
#[cfg(feature = "libsnark")]
|
||||
use std::io::BufRead;
|
||||
use std::io::{stdin, BufReader, BufWriter, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String;
|
||||
use zokrates_core::compile::compile;
|
||||
use zokrates_core::ir;
|
||||
#[cfg(feature = "libsnark")]
|
||||
use zokrates_core::ir::r1cs_program;
|
||||
#[cfg(feature = "libsnark")]
|
||||
use zokrates_core::proof_system::{ProofSystem, GM17, PGHR13};
|
||||
use zokrates_core::proof_system::*;
|
||||
use zokrates_field::field::{Field, FieldPrime};
|
||||
use zokrates_fs_resolver::resolve as fs_resolve;
|
||||
|
||||
|
@ -37,9 +30,8 @@ fn cli() -> Result<(), String> {
|
|||
const PROVING_KEY_DEFAULT_PATH: &str = "proving.key";
|
||||
const VERIFICATION_CONTRACT_DEFAULT_PATH: &str = "verifier.sol";
|
||||
const WITNESS_DEFAULT_PATH: &str = "witness";
|
||||
const VARIABLES_INFORMATION_KEY_DEFAULT_PATH: &str = "variables.inf";
|
||||
const JSON_PROOF_PATH: &str = "proof.json";
|
||||
let default_backend = env::var("ZOKRATES_BACKEND").unwrap_or(String::from("pghr13"));
|
||||
let default_scheme = env::var("ZOKRATES_PROVING_SCHEME").unwrap_or(String::from("g16"));
|
||||
|
||||
// cli specification using clap library
|
||||
let matches = App::new("ZoKrates")
|
||||
|
@ -99,23 +91,14 @@ fn cli() -> Result<(), String> {
|
|||
.required(false)
|
||||
.default_value(VERIFICATION_KEY_DEFAULT_PATH)
|
||||
)
|
||||
.arg(Arg::with_name("meta-information")
|
||||
.short("m")
|
||||
.long("meta-information")
|
||||
.help("Path of the file containing meta-information for variable transformation")
|
||||
.arg(Arg::with_name("proving-scheme")
|
||||
.short("s")
|
||||
.long("proving-scheme")
|
||||
.help("Proving scheme to use in the setup. Available options are G16 (default), PGHR13 and GM17")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(VARIABLES_INFORMATION_KEY_DEFAULT_PATH)
|
||||
)
|
||||
.arg(Arg::with_name("backend")
|
||||
.short("b")
|
||||
.long("backend")
|
||||
.help("Backend to use in the setup. Available options are PGHR13 and GM17")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(&default_backend)
|
||||
.default_value(&default_scheme)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("export-verifier")
|
||||
|
@ -137,14 +120,14 @@ fn cli() -> Result<(), String> {
|
|||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(VERIFICATION_CONTRACT_DEFAULT_PATH)
|
||||
).arg(Arg::with_name("backend")
|
||||
.short("b")
|
||||
.long("backend")
|
||||
.help("Backend to use to export the verifier. Available options are PGHR13 and GM17")
|
||||
).arg(Arg::with_name("proving-scheme")
|
||||
.short("s")
|
||||
.long("proving-scheme")
|
||||
.help("Proving scheme to use to export the verifier. Available options are G16 (default), PGHR13 and GM17")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(&default_backend)
|
||||
.default_value(&default_scheme)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("compute-witness")
|
||||
|
@ -200,22 +183,22 @@ fn cli() -> Result<(), String> {
|
|||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(JSON_PROOF_PATH)
|
||||
).arg(Arg::with_name("meta-information")
|
||||
).arg(Arg::with_name("input")
|
||||
.short("i")
|
||||
.long("meta-information")
|
||||
.help("Path of file containing meta information for variable transformation")
|
||||
.long("input")
|
||||
.help("Path of compiled code")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(VARIABLES_INFORMATION_KEY_DEFAULT_PATH)
|
||||
).arg(Arg::with_name("backend")
|
||||
.short("b")
|
||||
.long("backend")
|
||||
.help("Backend to use to generate the proof. Available options are PGHR13 and GM17")
|
||||
.default_value(FLATTENED_CODE_DEFAULT_PATH)
|
||||
).arg(Arg::with_name("proving-scheme")
|
||||
.short("s")
|
||||
.long("proving-scheme")
|
||||
.help("Proving scheme to use to generate the proof. Available options are G16 (default), PGHR13 and GM17")
|
||||
.value_name("FILE")
|
||||
.takes_value(true)
|
||||
.required(false)
|
||||
.default_value(&default_backend)
|
||||
.default_value(&default_scheme)
|
||||
)
|
||||
)
|
||||
.get_matches();
|
||||
|
@ -306,7 +289,7 @@ fn cli() -> Result<(), String> {
|
|||
let arguments: Vec<_> = match sub_matches.values_of("arguments") {
|
||||
// take inline arguments
|
||||
Some(p) => p
|
||||
.map(|x| FieldPrime::try_from_str(x).map_err(|_| x.to_string()))
|
||||
.map(|x| FieldPrime::try_from_dec_str(x).map_err(|_| x.to_string()))
|
||||
.collect(),
|
||||
// take stdin arguments
|
||||
None => {
|
||||
|
@ -318,7 +301,9 @@ fn cli() -> Result<(), String> {
|
|||
input.retain(|x| x != '\n');
|
||||
input
|
||||
.split(" ")
|
||||
.map(|x| FieldPrime::try_from_str(x).map_err(|_| x.to_string()))
|
||||
.map(|x| {
|
||||
FieldPrime::try_from_dec_str(x).map_err(|_| x.to_string())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
Err(_) => Err(String::from("???")),
|
||||
|
@ -349,15 +334,12 @@ fn cli() -> Result<(), String> {
|
|||
let output_file = File::create(&output_path)
|
||||
.map_err(|why| format!("couldn't create {}: {}", output_path.display(), why))?;
|
||||
|
||||
let mut bw = BufWriter::new(output_file);
|
||||
write!(&mut bw, "{}", witness)
|
||||
.map_err(|_| "Unable to write data to file.".to_string())?;
|
||||
bw.flush()
|
||||
.map_err(|_| "Unable to flush buffer.".to_string())?;
|
||||
witness
|
||||
.write(output_file)
|
||||
.map_err(|why| format!("could not save witness: {:?}", why))?;
|
||||
}
|
||||
#[cfg(feature = "libsnark")]
|
||||
("setup", Some(sub_matches)) => {
|
||||
let backend = get_backend(sub_matches.value_of("backend").unwrap())?;
|
||||
let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?;
|
||||
|
||||
println!("Performing setup...");
|
||||
|
||||
|
@ -371,55 +353,16 @@ fn cli() -> Result<(), String> {
|
|||
// print deserialized flattened program
|
||||
println!("{}", program);
|
||||
|
||||
// transform to R1CS
|
||||
let (variables, public_variables_count, a, b, c) = r1cs_program(program);
|
||||
|
||||
// write variables meta information to file
|
||||
let var_inf_path = Path::new(sub_matches.value_of("meta-information").unwrap());
|
||||
let var_inf_file = File::create(&var_inf_path)
|
||||
.map_err(|why| format!("couldn't open {}: {}", var_inf_path.display(), why))?;
|
||||
let mut bw = BufWriter::new(var_inf_file);
|
||||
|
||||
write!(
|
||||
&mut bw,
|
||||
"Private inputs offset:\n{}\n",
|
||||
public_variables_count
|
||||
)
|
||||
.map_err(|_| "Unable to write data to file.".to_string())?;
|
||||
write!(&mut bw, "R1CS variable order:\n")
|
||||
.map_err(|_| "Unable to write data to file.".to_string())?;
|
||||
|
||||
for var in &variables {
|
||||
write!(&mut bw, "{} ", var)
|
||||
.map_err(|_| "Unable to write data to file.".to_string())?;
|
||||
}
|
||||
write!(&mut bw, "\n").map_err(|_| "Unable to write data to file.".to_string())?;
|
||||
bw.flush()
|
||||
.map_err(|_| "Unable to flush buffer.".to_string())?;
|
||||
|
||||
// get paths for proving and verification keys
|
||||
let pk_path = sub_matches.value_of("proving-key-path").unwrap();
|
||||
let vk_path = sub_matches.value_of("verification-key-path").unwrap();
|
||||
|
||||
// run setup phase
|
||||
// number of inputs in the zkSNARK sense, i.e., input variables + output variables
|
||||
println!(
|
||||
"setup successful: {:?}",
|
||||
backend.setup(
|
||||
variables,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
public_variables_count - 1,
|
||||
pk_path,
|
||||
vk_path
|
||||
)
|
||||
);
|
||||
scheme.setup(program, pk_path, vk_path);
|
||||
}
|
||||
#[cfg(feature = "libsnark")]
|
||||
("export-verifier", Some(sub_matches)) => {
|
||||
{
|
||||
let backend = get_backend(sub_matches.value_of("backend").unwrap())?;
|
||||
let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?;
|
||||
|
||||
println!("Exporting verifier...");
|
||||
|
||||
|
@ -429,7 +372,7 @@ fn cli() -> Result<(), String> {
|
|||
.map_err(|why| format!("couldn't open {}: {}", input_path.display(), why))?;
|
||||
let reader = BufReader::new(input_file);
|
||||
|
||||
let verifier = backend.export_solidity_verifier(reader);
|
||||
let verifier = scheme.export_solidity_verifier(reader);
|
||||
|
||||
//write output file
|
||||
let output_path = Path::new(sub_matches.value_of("output").unwrap());
|
||||
|
@ -442,82 +385,34 @@ fn cli() -> Result<(), String> {
|
|||
println!("Finished exporting verifier.");
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "libsnark")]
|
||||
("generate-proof", Some(sub_matches)) => {
|
||||
println!("Generating proof...");
|
||||
|
||||
let backend = get_backend(sub_matches.value_of("backend").unwrap())?;
|
||||
let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?;
|
||||
|
||||
// deserialize witness
|
||||
let witness_path = Path::new(sub_matches.value_of("witness").unwrap());
|
||||
let witness_file = File::open(&witness_path)
|
||||
.map_err(|why| format!("couldn't open {}: {}", witness_path.display(), why))?;
|
||||
let witness_file = match File::open(&witness_path) {
|
||||
Ok(file) => file,
|
||||
Err(why) => panic!("couldn't open {}: {}", witness_path.display(), why),
|
||||
};
|
||||
|
||||
let reader = BufReader::new(witness_file);
|
||||
let mut lines = reader.lines();
|
||||
let mut witness_map = HashMap::new();
|
||||
|
||||
loop {
|
||||
match lines.next() {
|
||||
Some(Ok(ref x)) => {
|
||||
let pairs: Vec<&str> = x.split_whitespace().collect();
|
||||
witness_map.insert(
|
||||
pairs[0].to_string(),
|
||||
FieldPrime::from_dec_string(pairs[1].to_string()),
|
||||
);
|
||||
}
|
||||
None => break,
|
||||
Some(Err(err)) => return Err(format!("Error reading witness: {}", err)),
|
||||
}
|
||||
}
|
||||
|
||||
// determine variable order
|
||||
let var_inf_path = Path::new(sub_matches.value_of("meta-information").unwrap());
|
||||
let var_inf_file = File::open(&var_inf_path)
|
||||
.map_err(|why| format!("couldn't open {}: {}", var_inf_path.display(), why))?;
|
||||
let var_reader = BufReader::new(var_inf_file);
|
||||
let mut var_lines = var_reader.lines();
|
||||
|
||||
// get private inputs offset
|
||||
let private_inputs_offset;
|
||||
if let Some(Ok(ref o)) = var_lines.nth(1) {
|
||||
// consumes first 2 lines
|
||||
private_inputs_offset = o
|
||||
.parse()
|
||||
.map_err(|_| "Failed parsing private inputs offset")?;
|
||||
} else {
|
||||
return Err(format!("Error reading private inputs offset"));
|
||||
}
|
||||
|
||||
// get variables vector
|
||||
let mut variables: Vec<String> = Vec::new();
|
||||
if let Some(Ok(ref v)) = var_lines.nth(1) {
|
||||
let iter = v.split_whitespace();
|
||||
for i in iter {
|
||||
variables.push(i.to_string());
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Error reading variables"));
|
||||
}
|
||||
|
||||
println!("Using Witness: {:?}", witness_map);
|
||||
|
||||
let witness: Vec<_> = variables.iter().map(|x| witness_map[x].clone()).collect();
|
||||
|
||||
// split witness into public and private inputs at offset
|
||||
let mut public_inputs: Vec<_> = witness.clone();
|
||||
let private_inputs: Vec<_> = public_inputs.split_off(private_inputs_offset);
|
||||
|
||||
println!("Public inputs: {:?}", public_inputs);
|
||||
println!("Private inputs: {:?}", private_inputs);
|
||||
let witness = ir::Witness::read(witness_file)
|
||||
.map_err(|why| format!("could not load witness: {:?}", why))?;
|
||||
|
||||
let pk_path = sub_matches.value_of("provingkey").unwrap();
|
||||
let proof_path = sub_matches.value_of("proofpath").unwrap();
|
||||
|
||||
// run libsnark
|
||||
let program_path = Path::new(sub_matches.value_of("input").unwrap());
|
||||
let mut program_file = File::open(&program_path)
|
||||
.map_err(|why| format!("couldn't open {}: {}", program_path.display(), why))?;
|
||||
|
||||
let program: ir::Prog<FieldPrime> = deserialize_from(&mut program_file, Infinite)
|
||||
.map_err(|why| format!("{:?}", why))?;
|
||||
|
||||
println!(
|
||||
"generate-proof successful: {:?}",
|
||||
backend.generate_proof(pk_path, proof_path, public_inputs, private_inputs)
|
||||
scheme.generate_proof(program, witness, pk_path, proof_path)
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -525,11 +420,13 @@ fn cli() -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "libsnark")]
|
||||
fn get_backend(backend_str: &str) -> Result<&'static ProofSystem, String> {
|
||||
match backend_str.to_lowercase().as_ref() {
|
||||
fn get_scheme(scheme_str: &str) -> Result<&'static ProofSystem, String> {
|
||||
match scheme_str.to_lowercase().as_ref() {
|
||||
#[cfg(feature = "libsnark")]
|
||||
"pghr13" => Ok(&PGHR13 {}),
|
||||
#[cfg(feature = "libsnark")]
|
||||
"gm17" => Ok(&GM17 {}),
|
||||
"g16" => Ok(&G16 {}),
|
||||
s => Err(format!("Backend \"{}\" not supported", s)),
|
||||
}
|
||||
}
|
||||
|
@ -539,7 +436,6 @@ mod tests {
|
|||
extern crate glob;
|
||||
use self::glob::glob;
|
||||
use super::*;
|
||||
use zokrates_core::ir::r1cs_program;
|
||||
|
||||
#[test]
|
||||
fn examples() {
|
||||
|
@ -566,10 +462,8 @@ mod tests {
|
|||
.into_string()
|
||||
.unwrap();
|
||||
|
||||
let program_flattened: ir::Prog<FieldPrime> =
|
||||
let _: ir::Prog<FieldPrime> =
|
||||
compile(&mut reader, Some(location), Some(fs_resolve)).unwrap();
|
||||
|
||||
let (..) = r1cs_program(program_flattened);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -598,7 +492,6 @@ mod tests {
|
|||
let program_flattened: ir::Prog<FieldPrime> =
|
||||
compile(&mut reader, Some(location), Some(fs_resolve)).unwrap();
|
||||
|
||||
let (..) = r1cs_program(program_flattened.clone());
|
||||
let _ = program_flattened
|
||||
.execute(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
|
@ -631,8 +524,6 @@ mod tests {
|
|||
let program_flattened: ir::Prog<FieldPrime> =
|
||||
compile(&mut reader, Some(location), Some(fs_resolve)).unwrap();
|
||||
|
||||
let (..) = r1cs_program(program_flattened.clone());
|
||||
|
||||
let _ = program_flattened
|
||||
.execute(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
|
|
|
@ -1 +1 @@
|
|||
[1, 1, 1, 2, 2, 3, 4, 4, 4, 4]
|
||||
[1, 1, 1, 2, 3, 3, 3, 3]
|
|
@ -1,2 +1,2 @@
|
|||
def main(field[3] a, private field[2] b, field c, private field[4] d) -> (field[3], field[4]):
|
||||
return a, d
|
||||
def main(field[3] a, field b, private field[4] c) -> (field, field[3], field[4]):
|
||||
return b, a, c
|
|
@ -1,7 +1,8 @@
|
|||
~out_0 1
|
||||
~out_0 2
|
||||
~out_1 1
|
||||
~out_2 1
|
||||
~out_3 4
|
||||
~out_4 4
|
||||
~out_5 4
|
||||
~out_6 4
|
||||
~out_3 1
|
||||
~out_4 3
|
||||
~out_5 3
|
||||
~out_6 3
|
||||
~out_7 3
|
|
@ -11,28 +11,10 @@ mod integration {
|
|||
use std::io::prelude::*;
|
||||
use std::panic;
|
||||
use std::path::Path;
|
||||
|
||||
fn setup() {
|
||||
fs::create_dir(".tmp").unwrap();
|
||||
}
|
||||
|
||||
fn teardown() {
|
||||
fs::remove_dir_all(".tmp").unwrap();
|
||||
}
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn run_integration_tests() {
|
||||
// see https://medium.com/@ericdreichert/test-setup-and-teardown-in-rust-without-a-framework-ba32d97aa5ab
|
||||
setup();
|
||||
|
||||
let result = panic::catch_unwind(|| test_compile_and_witness_dir());
|
||||
|
||||
teardown();
|
||||
|
||||
assert!(result.is_ok())
|
||||
}
|
||||
|
||||
fn test_compile_and_witness_dir() {
|
||||
let dir = Path::new("./tests/code");
|
||||
if dir.is_dir() {
|
||||
|
@ -62,7 +44,8 @@ mod integration {
|
|||
arguments_path: &Path,
|
||||
expected_witness_path: &Path,
|
||||
) {
|
||||
let tmp_base = Path::new(".tmp/");
|
||||
let tmp_dir = TempDir::new(".tmp").unwrap();
|
||||
let tmp_base = tmp_dir.path();
|
||||
let test_case_path = tmp_base.join(program_name);
|
||||
let flattened_path = tmp_base.join(program_name).join("out");
|
||||
let witness_path = tmp_base.join(program_name).join("witness");
|
||||
|
@ -75,10 +58,6 @@ mod integration {
|
|||
.join(program_name)
|
||||
.join("proving")
|
||||
.with_extension("key");
|
||||
let variable_information_path = tmp_base
|
||||
.join(program_name)
|
||||
.join("variables")
|
||||
.with_extension("inf");
|
||||
let verification_contract_path = tmp_base
|
||||
.join(program_name)
|
||||
.join("verifier")
|
||||
|
@ -191,72 +170,72 @@ mod integration {
|
|||
}
|
||||
|
||||
#[cfg(feature = "libsnark")]
|
||||
{
|
||||
for backend in &["pghr13", "gm17"] {
|
||||
// SETUP
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"setup",
|
||||
"-i",
|
||||
flattened_path.to_str().unwrap(),
|
||||
"-p",
|
||||
proving_key_path.to_str().unwrap(),
|
||||
"-v",
|
||||
verification_key_path.to_str().unwrap(),
|
||||
"-m",
|
||||
variable_information_path.to_str().unwrap(),
|
||||
"--backend",
|
||||
backend,
|
||||
])
|
||||
let schemes = ["pghr13", "gm17", "g16"];
|
||||
#[cfg(not(feature = "libsnark"))]
|
||||
let schemes = ["g16"];
|
||||
|
||||
for scheme in &schemes {
|
||||
// SETUP
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"setup",
|
||||
"-i",
|
||||
flattened_path.to_str().unwrap(),
|
||||
"-p",
|
||||
proving_key_path.to_str().unwrap(),
|
||||
"-v",
|
||||
verification_key_path.to_str().unwrap(),
|
||||
"--proving-scheme",
|
||||
scheme,
|
||||
])
|
||||
.succeeds()
|
||||
.unwrap();
|
||||
|
||||
// EXPORT-VERIFIER
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"export-verifier",
|
||||
"-i",
|
||||
verification_key_path.to_str().unwrap(),
|
||||
"-o",
|
||||
verification_contract_path.to_str().unwrap(),
|
||||
"--proving-scheme",
|
||||
scheme,
|
||||
])
|
||||
.succeeds()
|
||||
.unwrap();
|
||||
|
||||
let mut verifier_file = File::open(&verification_contract_path).unwrap();
|
||||
let mut verifier_string = String::new();
|
||||
verifier_file.read_to_string(&mut verifier_string).unwrap();
|
||||
|
||||
let solc_json_input = format!(
|
||||
r#"{{"language": "Solidity", "sources": {{ "this": {{ "content": {:?} }} }} }}"#,
|
||||
verifier_string
|
||||
);
|
||||
|
||||
assert_cli::Assert::command(&["solcjs", "--standard-json"])
|
||||
.stdin(&solc_json_input)
|
||||
.succeeds()
|
||||
.stdout()
|
||||
.doesnt_contain(r#""severity":"error""#)
|
||||
.unwrap();
|
||||
|
||||
// EXPORT-VERIFIER
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"export-verifier",
|
||||
"-i",
|
||||
verification_key_path.to_str().unwrap(),
|
||||
"-o",
|
||||
verification_contract_path.to_str().unwrap(),
|
||||
"--backend",
|
||||
backend,
|
||||
])
|
||||
.succeeds()
|
||||
.unwrap();
|
||||
|
||||
let mut verifier_file = File::open(&verification_contract_path).unwrap();
|
||||
let mut verifier_string = String::new();
|
||||
verifier_file.read_to_string(&mut verifier_string).unwrap();
|
||||
|
||||
let solc_json_input = format!(
|
||||
r#"{{"language": "Solidity", "sources": {{ "this": {{ "content": {:?} }} }} }}"#,
|
||||
verifier_string
|
||||
);
|
||||
|
||||
assert_cli::Assert::command(&["solcjs", "--standard-json"])
|
||||
.stdin(&solc_json_input)
|
||||
.succeeds()
|
||||
.stdout()
|
||||
.doesnt_contain(r#""severity":"error""#)
|
||||
.unwrap();
|
||||
|
||||
// GENERATE-PROOF
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"generate-proof",
|
||||
"-w",
|
||||
witness_path.to_str().unwrap(),
|
||||
"-p",
|
||||
proving_key_path.to_str().unwrap(),
|
||||
"-i",
|
||||
variable_information_path.to_str().unwrap(),
|
||||
"--backend",
|
||||
backend,
|
||||
])
|
||||
.succeeds()
|
||||
.unwrap();
|
||||
}
|
||||
// GENERATE-PROOF
|
||||
assert_cli::Assert::command(&[
|
||||
"../target/release/zokrates",
|
||||
"generate-proof",
|
||||
"-i",
|
||||
flattened_path.to_str().unwrap(),
|
||||
"-w",
|
||||
witness_path.to_str().unwrap(),
|
||||
"-p",
|
||||
proving_key_path.to_str().unwrap(),
|
||||
"--proving-scheme",
|
||||
scheme,
|
||||
])
|
||||
.succeeds()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,15 @@ serde_bytes = "0.10"
|
|||
bincode = "0.8.0"
|
||||
regex = "0.2"
|
||||
bimap = "0.1"
|
||||
bellman = { git = "https://github.com/matterinc/bellman", tag = "0.2.0" }
|
||||
pairing = { git = "https://github.com/matterinc/pairing", tag = "0.16.2" }
|
||||
ff = { git = 'https://github.com/matterinc/ff', features = ["derive"], tag = "0.5" }
|
||||
zokrates_field = { version = "0.3.0", path = "../zokrates_field" }
|
||||
rand = "0.4"
|
||||
wasmi = "0.4.2"
|
||||
parity-wasm = "0.35.3"
|
||||
rustc-hex = "1.0"
|
||||
csv = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
glob = "0.2.11"
|
||||
|
|
|
@ -117,32 +117,8 @@ void serializeVerificationKeyToFile(r1cs_se_ppzksnark_verification_key<libff::al
|
|||
fh.close();
|
||||
}
|
||||
|
||||
// compliant with solidty verification example
|
||||
void exportVerificationKey(r1cs_se_ppzksnark_keypair<libff::alt_bn128_pp> keypair){
|
||||
auto vk = keypair.vk;
|
||||
unsigned queryLength = vk.query.size();
|
||||
|
||||
cout << "\t\tvk.H = " << outputPointG2AffineAsHex(vk.H) << endl;
|
||||
cout << "\t\tvk.Galpha = " << outputPointG1AffineAsHex(vk.G_alpha) << endl;
|
||||
cout << "\t\tvk.Hbeta = " << outputPointG2AffineAsHex(vk.H_beta) << endl;
|
||||
cout << "\t\tvk.Ggamma = " << outputPointG1AffineAsHex(vk.G_gamma) << endl;
|
||||
cout << "\t\tvk.Hgamma = " << outputPointG2AffineAsHex(vk.H_gamma) << endl;
|
||||
cout << "\t\tvk.query.len() = " << queryLength << endl;
|
||||
for (size_t i = 0; i < queryLength; ++i)
|
||||
{
|
||||
auto vkqueryi = outputPointG1AffineAsHex(vk.query[i]);
|
||||
cout << "\t\tvk.query[" << i << "] = " << vkqueryi << endl;
|
||||
}
|
||||
cout << "\t\t}" << endl;
|
||||
}
|
||||
|
||||
void printProof(r1cs_se_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* proof_path, const uint8_t* public_inputs,
|
||||
void exportProof(r1cs_se_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* proof_path, const uint8_t* public_inputs,
|
||||
int public_inputs_length){
|
||||
cout << "Proof:"<< endl;
|
||||
cout << "A = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.A)<< ");" << endl;
|
||||
cout << "B = Pairing.G2Point(" << outputPointG2AffineAsHex(proof.B)<< ");" << endl;
|
||||
cout << "C = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.C)<< ");" << endl;
|
||||
|
||||
//create JSON file
|
||||
std::stringstream ss;
|
||||
ss << "{" << "\n";
|
||||
|
@ -160,7 +136,7 @@ void printProof(r1cs_se_ppzksnark_proof<libff::alt_bn128_pp> proof, const char*
|
|||
if(i!=1){
|
||||
ss << ",";
|
||||
}
|
||||
ss << libsnarkBigintFromBytes(public_inputs + i*32);
|
||||
ss << HexStringFromLibsnarkBigint(libsnarkBigintFromBytes(public_inputs + i*32));
|
||||
}
|
||||
ss << "]" << "\n";
|
||||
ss << "}" << "\n";
|
||||
|
@ -170,16 +146,6 @@ void printProof(r1cs_se_ppzksnark_proof<libff::alt_bn128_pp> proof, const char*
|
|||
writeToFile(proof_path, s);
|
||||
}
|
||||
|
||||
// compliant with solidty verification example
|
||||
void exportInput(r1cs_primary_input<libff::Fr<libff::alt_bn128_pp>> input){
|
||||
cout << "\tInput in Solidity compliant format:{" << endl;
|
||||
for (size_t i = 0; i < input.size(); ++i)
|
||||
{
|
||||
cout << "\t\tinput[" << i << "] = " << HexStringFromLibsnarkBigint(input[i].as_bigint()) << ";" << endl;
|
||||
}
|
||||
cout << "\t\t}" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool _gm17_setup(const uint8_t* A, const uint8_t* B, const uint8_t* C, int A_len, int B_len, int C_len, int constraints, int variables, int inputs, const char* pk_path, const char* vk_path)
|
||||
|
@ -203,9 +169,6 @@ bool _gm17_setup(const uint8_t* A, const uint8_t* B, const uint8_t* C, int A_len
|
|||
gm17::serializeProvingKeyToFile(keypair.pk, pk_path);
|
||||
gm17::serializeVerificationKeyToFile(keypair.vk, vk_path);
|
||||
|
||||
// Print VerificationKey in Solidity compatible format
|
||||
gm17::exportVerificationKey(keypair);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -240,9 +203,7 @@ bool _gm17_generate_proof(const char* pk_path, const char* proof_path, const uin
|
|||
// Proof Generation
|
||||
auto proof = r1cs_se_ppzksnark_prover<libff::alt_bn128_pp>(pk, primary_input, auxiliary_input);
|
||||
|
||||
// print proof
|
||||
gm17::printProof(proof, proof_path, public_inputs, public_inputs_length);
|
||||
// TODO? print inputs
|
||||
gm17::exportProof(proof, proof_path, public_inputs, public_inputs_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -120,42 +120,8 @@ void serializeVerificationKeyToFile(r1cs_ppzksnark_verification_key<libff::alt_b
|
|||
fh.close();
|
||||
}
|
||||
|
||||
// compliant with solidty verification example
|
||||
void exportVerificationKey(r1cs_ppzksnark_keypair<libff::alt_bn128_pp> keypair){
|
||||
auto vk = keypair.vk;
|
||||
unsigned icLength = vk.encoded_IC_query.rest.indices.size() + 1;
|
||||
|
||||
cout << "\tVerification key in Solidity compliant format:{" << endl;
|
||||
cout << "\t\tvk.A = Pairing.G2Point(" << outputPointG2AffineAsHex(vk.alphaA_g2) << ");" << endl;
|
||||
cout << "\t\tvk.B = Pairing.G1Point(" << outputPointG1AffineAsHex(vk.alphaB_g1) << ");" << endl;
|
||||
cout << "\t\tvk.C = Pairing.G2Point(" << outputPointG2AffineAsHex(vk.alphaC_g2) << ");" << endl;
|
||||
cout << "\t\tvk.gamma = Pairing.G2Point(" << outputPointG2AffineAsHex(vk.gamma_g2) << ");" << endl;
|
||||
cout << "\t\tvk.gammaBeta1 = Pairing.G1Point(" << outputPointG1AffineAsHex(vk.gamma_beta_g1) << ");" << endl;
|
||||
cout << "\t\tvk.gammaBeta2 = Pairing.G2Point(" << outputPointG2AffineAsHex(vk.gamma_beta_g2) << ");" << endl;
|
||||
cout << "\t\tvk.Z = Pairing.G2Point(" << outputPointG2AffineAsHex(vk.rC_Z_g2) << ");" << endl;
|
||||
cout << "\t\tvk.IC = new Pairing.G1Point[](" << icLength << ");" << endl;
|
||||
cout << "\t\tvk.IC[0] = Pairing.G1Point(" << outputPointG1AffineAsHex(vk.encoded_IC_query.first) << ");" << endl;
|
||||
for (size_t i = 1; i < icLength; ++i)
|
||||
{
|
||||
auto vkICi = outputPointG1AffineAsHex(vk.encoded_IC_query.rest.values[i - 1]);
|
||||
cout << "\t\tvk.IC[" << i << "] = Pairing.G1Point(" << vkICi << ");" << endl;
|
||||
}
|
||||
cout << "\t\t}" << endl;
|
||||
|
||||
}
|
||||
|
||||
void printProof(r1cs_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* proof_path, const uint8_t* public_inputs,
|
||||
void exportProof(r1cs_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* proof_path, const uint8_t* public_inputs,
|
||||
int public_inputs_length){
|
||||
cout << "Proof:"<< endl;
|
||||
cout << "A = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_A.g)<< ");" << endl;
|
||||
cout << "A_p = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_A.h)<< ");" << endl;
|
||||
cout << "B = Pairing.G2Point(" << outputPointG2AffineAsHex(proof.g_B.g)<< ");" << endl;
|
||||
cout << "B_p = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_B.h)<<");" << endl;
|
||||
cout << "C = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_C.g)<< ");" << endl;
|
||||
cout << "C_p = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_C.h)<<");" << endl;
|
||||
cout << "H = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_H)<<");"<< endl;
|
||||
cout << "K = Pairing.G1Point(" << outputPointG1AffineAsHex(proof.g_K)<<");"<< endl;
|
||||
|
||||
//create JSON file
|
||||
std::stringstream ss;
|
||||
ss << "{" << "\n";
|
||||
|
@ -178,7 +144,7 @@ void printProof(r1cs_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* pro
|
|||
if(i!=1){
|
||||
ss << ",";
|
||||
}
|
||||
ss << libsnarkBigintFromBytes(public_inputs + i*32);
|
||||
ss << HexStringFromLibsnarkBigint(libsnarkBigintFromBytes(public_inputs + i*32));
|
||||
}
|
||||
ss << "]" << "\n";
|
||||
ss << "}" << "\n";
|
||||
|
@ -190,16 +156,6 @@ void printProof(r1cs_ppzksnark_proof<libff::alt_bn128_pp> proof, const char* pro
|
|||
|
||||
}
|
||||
|
||||
// compliant with solidty verification example
|
||||
void exportInput(r1cs_primary_input<libff::Fr<libff::alt_bn128_pp>> input){
|
||||
cout << "\tInput in Solidity compliant format:{" << endl;
|
||||
for (size_t i = 0; i < input.size(); ++i)
|
||||
{
|
||||
cout << "\t\tinput[" << i << "] = " << HexStringFromLibsnarkBigint(input[i].as_bigint()) << ";" << endl;
|
||||
}
|
||||
cout << "\t\t}" << endl;
|
||||
}
|
||||
|
||||
bool _pghr13_setup(const uint8_t* A, const uint8_t* B, const uint8_t* C, int A_len, int B_len, int C_len, int constraints, int variables, int inputs, const char* pk_path, const char* vk_path)
|
||||
{
|
||||
libff::inhibit_profiling_info = true;
|
||||
|
@ -221,9 +177,6 @@ bool _pghr13_setup(const uint8_t* A, const uint8_t* B, const uint8_t* C, int A_l
|
|||
pghr13::serializeProvingKeyToFile(keypair.pk, pk_path);
|
||||
pghr13::serializeVerificationKeyToFile(keypair.vk, vk_path);
|
||||
|
||||
// Print VerificationKey in Solidity compatible format
|
||||
pghr13::exportVerificationKey(keypair);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -258,9 +211,7 @@ bool _pghr13_generate_proof(const char* pk_path, const char* proof_path, const u
|
|||
// Proof Generation
|
||||
auto proof = r1cs_ppzksnark_prover<libff::alt_bn128_pp>(pk, primary_input, auxiliary_input);
|
||||
|
||||
// print proof
|
||||
pghr13::printProof(proof, proof_path, public_inputs, public_inputs_length);
|
||||
// TODO? print inputs
|
||||
pghr13::exportProof(proof, proof_path, public_inputs, public_inputs_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ libff::bigint<libff::alt_bn128_r_limbs> libsnarkBigintFromBytes(const uint8_t* _
|
|||
x.data[3 - i] |= uint64_t(_x[i * 8 + j]) << (8 * (7-j));
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,30 @@ impl FlatVariable {
|
|||
assert!(self.id > 0);
|
||||
(self.id as usize) - 1
|
||||
}
|
||||
|
||||
pub fn try_from_human_readable(s: &str) -> Result<Self, &str> {
|
||||
if s == "~one" {
|
||||
return Ok(FlatVariable::one());
|
||||
}
|
||||
|
||||
let mut public = s.split("~out_");
|
||||
match public.nth(1) {
|
||||
Some(v) => {
|
||||
let v = v.parse().map_err(|_| s)?;
|
||||
Ok(FlatVariable::public(v))
|
||||
}
|
||||
None => {
|
||||
let mut private = s.split("_");
|
||||
match private.nth(1) {
|
||||
Some(v) => {
|
||||
let v = v.parse().map_err(|_| s)?;
|
||||
Ok(FlatVariable::new(v))
|
||||
}
|
||||
None => Err(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FlatVariable {
|
||||
|
|
|
@ -1,50 +1,12 @@
|
|||
use flat_absy::flat_variable::FlatVariable;
|
||||
use helpers::Executable;
|
||||
use ir::*;
|
||||
use ir::{LinComb, Prog, QuadComb, Statement, Witness};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
pub type ExecutionResult<T> = Result<Witness<T>, Error>;
|
||||
|
||||
pub struct Witness<T: Field>(BTreeMap<FlatVariable, T>);
|
||||
|
||||
impl<T: Field> Witness<T> {
|
||||
pub fn return_values(&self) -> Vec<&T> {
|
||||
let out = self
|
||||
.0
|
||||
.iter()
|
||||
.filter(|(k, _)| k.is_output())
|
||||
.collect::<HashMap<_, _>>();
|
||||
(0..out.len())
|
||||
.map(|i| *out.get(&FlatVariable::public(i)).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn format_outputs(&self) -> String {
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|(variable, value)| match variable {
|
||||
variable if variable.is_output() => Some(format!("{} {}", variable, value)),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Field> fmt::Display for Witness<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{} {}", k, v.to_dec_string()))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Field> Prog<T> {
|
||||
pub fn execute<U: Into<T> + Clone>(&self, inputs: &Vec<U>) -> ExecutionResult<T> {
|
||||
let main = &self.main;
|
||||
|
@ -59,12 +21,12 @@ impl<T: Field> Prog<T> {
|
|||
match statement {
|
||||
Statement::Constraint(quad, lin) => match lin.is_assignee(&witness) {
|
||||
true => {
|
||||
let val = quad.evaluate(&witness);
|
||||
let val = quad.evaluate(&witness).unwrap();
|
||||
witness.insert(lin.0.iter().next().unwrap().0.clone(), val);
|
||||
}
|
||||
false => {
|
||||
let lhs_value = quad.evaluate(&witness);
|
||||
let rhs_value = lin.evaluate(&witness);
|
||||
let lhs_value = quad.evaluate(&witness).unwrap();
|
||||
let rhs_value = lin.evaluate(&witness).unwrap();
|
||||
if lhs_value != rhs_value {
|
||||
return Err(Error::UnsatisfiedConstraint {
|
||||
left: lhs_value.to_dec_string(),
|
||||
|
@ -74,8 +36,11 @@ impl<T: Field> Prog<T> {
|
|||
}
|
||||
},
|
||||
Statement::Directive(ref d) => {
|
||||
let input_values: Vec<T> =
|
||||
d.inputs.iter().map(|i| i.evaluate(&witness)).collect();
|
||||
let input_values: Vec<T> = d
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|i| i.evaluate(&witness).unwrap())
|
||||
.collect();
|
||||
match d.helper.execute(&input_values) {
|
||||
Ok(res) => {
|
||||
for (i, o) in d.outputs.iter().enumerate() {
|
||||
|
@ -105,14 +70,15 @@ impl<T: Field> Prog<T> {
|
|||
}
|
||||
|
||||
impl<T: Field> LinComb<T> {
|
||||
fn evaluate(&self, witness: &BTreeMap<FlatVariable, T>) -> T {
|
||||
fn evaluate(&self, witness: &BTreeMap<FlatVariable, T>) -> Result<T, ()> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(var, val)| witness.get(var).unwrap().clone() * val)
|
||||
.fold(T::from(0), |acc, t| acc + t)
|
||||
.map(|(var, mult)| witness.get(var).map(|v| v.clone() * mult).ok_or(())) // get each term
|
||||
.collect::<Result<Vec<_>, _>>() // fail if any term isn't found
|
||||
.map(|v| v.iter().fold(T::from(0), |acc, t| acc + t)) // return the sum
|
||||
}
|
||||
|
||||
fn is_assignee(&self, witness: &BTreeMap<FlatVariable, T>) -> bool {
|
||||
fn is_assignee<U>(&self, witness: &BTreeMap<FlatVariable, U>) -> bool {
|
||||
self.0.iter().count() == 1
|
||||
&& self.0.iter().next().unwrap().1 == &T::from(1)
|
||||
&& !witness.contains_key(self.0.iter().next().unwrap().0)
|
||||
|
@ -120,8 +86,10 @@ impl<T: Field> LinComb<T> {
|
|||
}
|
||||
|
||||
impl<T: Field> QuadComb<T> {
|
||||
fn evaluate(&self, witness: &BTreeMap<FlatVariable, T>) -> T {
|
||||
self.left.evaluate(&witness) * self.right.evaluate(&witness)
|
||||
pub fn evaluate(&self, witness: &BTreeMap<FlatVariable, T>) -> Result<T, ()> {
|
||||
let left = self.left.evaluate(&witness)?;
|
||||
let right = self.right.evaluate(&witness)?;
|
||||
Ok(left * right)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
use flat_absy::flat_parameter::FlatParameter;
|
||||
use flat_absy::FlatVariable;
|
||||
use helpers::Helper;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
mod expression;
|
||||
mod from_flat;
|
||||
mod interpreter;
|
||||
mod witness;
|
||||
|
||||
use self::expression::LinComb;
|
||||
pub use self::expression::LinComb;
|
||||
use self::expression::QuadComb;
|
||||
|
||||
pub use self::interpreter::Error;
|
||||
pub use self::interpreter::ExecutionResult;
|
||||
pub use self::interpreter::{Error, ExecutionResult};
|
||||
pub use self::witness::Witness;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Statement<T: Field> {
|
||||
|
@ -137,119 +136,6 @@ impl<T: Field> fmt::Display for Prog<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the index of `var` in `variables`, adding `var` with incremented index if it not yet exists.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `variables` - A mutual map that maps all existing variables to their index.
|
||||
/// * `var` - Variable to be searched for.
|
||||
pub fn provide_variable_idx(
|
||||
variables: &mut HashMap<FlatVariable, usize>,
|
||||
var: &FlatVariable,
|
||||
) -> usize {
|
||||
let index = variables.len();
|
||||
*variables.entry(*var).or_insert(index)
|
||||
}
|
||||
|
||||
/// Calculates one R1CS row representation of a program and returns (V, A, B, C) so that:
|
||||
/// * `V` contains all used variables and the index in the vector represents the used number in `A`, `B`, `C`
|
||||
/// * `<A,x>*<B,x> = <C,x>` for a witness `x`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `prog` - The program the representation is calculated for.
|
||||
pub fn r1cs_program<T: Field>(
|
||||
prog: Prog<T>,
|
||||
) -> (
|
||||
Vec<FlatVariable>,
|
||||
usize,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
) {
|
||||
let mut variables: HashMap<FlatVariable, usize> = HashMap::new();
|
||||
provide_variable_idx(&mut variables, &FlatVariable::one());
|
||||
|
||||
for x in prog
|
||||
.main
|
||||
.arguments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(index, _)| !prog.private[*index])
|
||||
{
|
||||
provide_variable_idx(&mut variables, &x.1);
|
||||
}
|
||||
|
||||
//Only the main function is relevant in this step, since all calls to other functions were resolved during flattening
|
||||
let main = prog.main;
|
||||
|
||||
//~out are added after main's arguments as we want variables (columns)
|
||||
//in the r1cs to be aligned like "public inputs | private inputs"
|
||||
let main_return_count = main.returns.len();
|
||||
|
||||
for i in 0..main_return_count {
|
||||
provide_variable_idx(&mut variables, &FlatVariable::public(i));
|
||||
}
|
||||
|
||||
// position where private part of witness starts
|
||||
let private_inputs_offset = variables.len();
|
||||
|
||||
// first pass through statements to populate `variables`
|
||||
for (quad, lin) in main.statements.iter().filter_map(|s| match s {
|
||||
Statement::Constraint(quad, lin) => Some((quad, lin)),
|
||||
Statement::Directive(..) => None,
|
||||
}) {
|
||||
for (k, _) in &quad.left.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
for (k, _) in &quad.right.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
for (k, _) in &lin.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
}
|
||||
|
||||
let mut a = vec![];
|
||||
let mut b = vec![];
|
||||
let mut c = vec![];
|
||||
|
||||
// second pass to convert program to raw sparse vectors
|
||||
for (quad, lin) in main.statements.into_iter().filter_map(|s| match s {
|
||||
Statement::Constraint(quad, lin) => Some((quad, lin)),
|
||||
Statement::Directive(..) => None,
|
||||
}) {
|
||||
a.push(
|
||||
quad.left
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
b.push(
|
||||
quad.right
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
c.push(
|
||||
lin.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert map back into list ordered by index
|
||||
let mut variables_list = vec![FlatVariable::new(0); variables.len()];
|
||||
for (k, v) in variables.drain() {
|
||||
assert_eq!(variables_list[v], FlatVariable::new(0));
|
||||
mem::replace(&mut variables_list[v], k);
|
||||
}
|
||||
(variables_list, private_inputs_offset, a, b, c)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
166
zokrates_core/src/ir/witness.rs
Normal file
166
zokrates_core/src/ir/witness.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use flat_absy::FlatVariable;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Witness<T: Field>(pub BTreeMap<FlatVariable, T>);
|
||||
|
||||
impl<T: Field> Witness<T> {
|
||||
pub fn return_values(&self) -> Vec<T> {
|
||||
let out = self
|
||||
.0
|
||||
.iter()
|
||||
.filter(|(k, _)| k.is_output())
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
(0..out.len())
|
||||
.map(|i| *out.get(&FlatVariable::public(i)).unwrap())
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn format_outputs(&self) -> String {
|
||||
self.0
|
||||
.iter()
|
||||
.filter_map(|(variable, value)| match variable {
|
||||
variable if variable.is_output() => Some(format!("{} {}", variable, value)),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
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);
|
||||
|
||||
// Write each line of the witness to the file
|
||||
for (variable, value) in &self.0 {
|
||||
wtr.serialize((variable.to_string(), value.to_dec_string()))?;
|
||||
}
|
||||
|
||||
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 map = rdr
|
||||
.deserialize::<(String, String)>()
|
||||
.map(|r| {
|
||||
r.map(|(variable, value)| {
|
||||
let variable =
|
||||
FlatVariable::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<FlatVariable, T>>>()?;
|
||||
|
||||
Ok(Witness(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Field> fmt::Display for Witness<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{} {}", k, v.to_dec_string()))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use zokrates_field::field::FieldPrime;
|
||||
|
||||
mod io {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize() {
|
||||
let w = Witness(
|
||||
vec![
|
||||
(FlatVariable::new(42), FieldPrime::from(42)),
|
||||
(FlatVariable::public(8), FieldPrime::from(8)),
|
||||
(FlatVariable::one(), FieldPrime::from(1)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let mut buff = Cursor::new(vec![]);
|
||||
|
||||
w.write(&mut buff).unwrap();
|
||||
buff.set_position(0);
|
||||
|
||||
let r = Witness::read(buff).unwrap();
|
||||
|
||||
assert_eq!(w, r);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_value() {
|
||||
let mut buff = Cursor::new(vec![]);
|
||||
|
||||
buff.write("_1 123bug".as_ref()).unwrap();
|
||||
buff.set_position(0);
|
||||
|
||||
assert!(Witness::<FieldPrime>::read(buff).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrong_variable() {
|
||||
let mut buff = Cursor::new(vec![]);
|
||||
|
||||
buff.write("_1bug 123".as_ref()).unwrap();
|
||||
buff.set_position(0);
|
||||
|
||||
assert!(Witness::<FieldPrime>::read(buff).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_csv() {
|
||||
let mut buff = Cursor::new(vec![]);
|
||||
buff.write("whatwhat".as_ref()).unwrap();
|
||||
buff.set_position(0);
|
||||
|
||||
assert!(Witness::<FieldPrime>::read(buff).is_err());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,8 +7,11 @@ extern crate serde; // serialization deserialization
|
|||
extern crate serde_json;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate bellman;
|
||||
extern crate bimap;
|
||||
extern crate bincode;
|
||||
extern crate ff;
|
||||
extern crate pairing;
|
||||
#[cfg(feature = "wasm")]
|
||||
extern crate parity_wasm;
|
||||
extern crate regex;
|
||||
|
@ -38,5 +41,4 @@ pub mod flat_absy;
|
|||
pub mod ir;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub mod libsnark;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub mod proof_system;
|
||||
|
|
|
@ -100,7 +100,7 @@ mod tests {
|
|||
)
|
||||
.unwrap()
|
||||
.to_bytes_le(),
|
||||
FieldPrime::try_from_str(
|
||||
FieldPrime::try_from_dec_str(
|
||||
"5472060717959818805561601436314318772174077789324455915672259473661306552146"
|
||||
)
|
||||
.unwrap()
|
||||
|
@ -112,13 +112,13 @@ mod tests {
|
|||
fn serialization_bin() {
|
||||
assert_eq!(
|
||||
BigUint::parse_bytes(b"110000011001000100111001110010111000010011000110100000001010011011100001010000010001011011011010000001100000010101100001011101100101111000000101101010100100010110100001110001110010101000110100111100001000001000110000010110110110000111110011111101010010",2).unwrap().to_bytes_le(),
|
||||
FieldPrime::try_from_str("5472060717959818805561601436314318772174077789324455915672259473661306552146").unwrap().into_byte_vector()
|
||||
FieldPrime::try_from_dec_str("5472060717959818805561601436314318772174077789324455915672259473661306552146").unwrap().into_byte_vector()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_to_array() {
|
||||
let byte_vector: Vec<u8> = FieldPrime::try_from_str(
|
||||
let byte_vector: Vec<u8> = FieldPrime::try_from_dec_str(
|
||||
"5472060717959818805561601436314318772174077789324455915672259473661306552146",
|
||||
)
|
||||
.unwrap()
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn parse_num<T: Field>(input: &String, pos: &Position) -> (Token<T>, String,
|
|||
}
|
||||
assert!(end > 0);
|
||||
(
|
||||
Token::Num(T::try_from_str(&input[0..end]).unwrap()),
|
||||
Token::Num(T::try_from_dec_str(&input[0..end]).unwrap()),
|
||||
input[end..].to_string(),
|
||||
Position {
|
||||
line: pos.line,
|
||||
|
|
326
zokrates_core/src/proof_system/bn128/g16.rs
Normal file
326
zokrates_core/src/proof_system/bn128/g16.rs
Normal file
|
@ -0,0 +1,326 @@
|
|||
use bellman::groth16::Parameters;
|
||||
use ir;
|
||||
use proof_system::bn128::utils::bellman::Computation;
|
||||
use proof_system::bn128::utils::solidity::{SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB};
|
||||
use proof_system::ProofSystem;
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::path::PathBuf;
|
||||
use zokrates_field::field::FieldPrime;
|
||||
|
||||
const G16_WARNING: &str = "WARNING: You are using the Groth16 scheme which is subject to malleability. See zokrates.github.io/reference/schemes.html#groth16-malleability for implications.";
|
||||
|
||||
pub struct G16 {}
|
||||
impl ProofSystem for G16 {
|
||||
fn setup(&self, program: ir::Prog<FieldPrime>, pk_path: &str, vk_path: &str) {
|
||||
std::env::set_var("BELLMAN_VERBOSE", "0");
|
||||
|
||||
println!("{}", G16_WARNING);
|
||||
|
||||
let parameters = Computation::without_witness(program).setup();
|
||||
let parameters_file = File::create(PathBuf::from(pk_path)).unwrap();
|
||||
parameters.write(parameters_file).unwrap();
|
||||
let mut vk_file = File::create(PathBuf::from(vk_path)).unwrap();
|
||||
vk_file
|
||||
.write(serialize::serialize_vk(parameters.vk).as_ref())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn generate_proof(
|
||||
&self,
|
||||
program: ir::Prog<FieldPrime>,
|
||||
witness: ir::Witness<FieldPrime>,
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
) -> bool {
|
||||
std::env::set_var("BELLMAN_VERBOSE", "0");
|
||||
|
||||
println!("{}", G16_WARNING);
|
||||
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
let parameters_file = File::open(PathBuf::from(pk_path)).unwrap();
|
||||
|
||||
let params = Parameters::read(parameters_file, true).unwrap();
|
||||
|
||||
let proof = computation.clone().prove(¶ms);
|
||||
|
||||
let mut proof_file = File::create(PathBuf::from(proof_path)).unwrap();
|
||||
write!(
|
||||
proof_file,
|
||||
"{}",
|
||||
serialize::serialize_proof(&proof, &computation.public_inputs_values())
|
||||
)
|
||||
.unwrap();
|
||||
true
|
||||
}
|
||||
|
||||
fn export_solidity_verifier(&self, reader: BufReader<File>) -> String {
|
||||
let mut lines = reader.lines();
|
||||
|
||||
let mut template_text = String::from(CONTRACT_TEMPLATE);
|
||||
let gamma_abc_template = String::from("vk.gammaABC[index] = Pairing.G1Point(points);"); //copy this for each entry
|
||||
|
||||
//replace things in template
|
||||
let vk_regex = Regex::new(r#"(<%vk_[^i%]*%>)"#).unwrap();
|
||||
let vk_gamma_abc_len_regex = Regex::new(r#"(<%vk_gammaABC_length%>)"#).unwrap();
|
||||
let vk_gamma_abc_index_regex = Regex::new(r#"index"#).unwrap();
|
||||
let vk_gamma_abc_points_regex = Regex::new(r#"points"#).unwrap();
|
||||
let vk_gamma_abc_repeat_regex = Regex::new(r#"(<%vk_gammaABC_pts%>)"#).unwrap();
|
||||
let vk_input_len_regex = Regex::new(r#"(<%vk_input_length%>)"#).unwrap();
|
||||
|
||||
for _ in 0..4 {
|
||||
let current_line: String = lines
|
||||
.next()
|
||||
.expect("Unexpected end of file in verification key!")
|
||||
.unwrap();
|
||||
let current_line_split: Vec<&str> = current_line.split("=").collect();
|
||||
assert_eq!(current_line_split.len(), 2);
|
||||
template_text = vk_regex
|
||||
.replace(template_text.as_str(), current_line_split[1].trim())
|
||||
.into_owned();
|
||||
}
|
||||
|
||||
let current_line: String = lines
|
||||
.next()
|
||||
.expect("Unexpected end of file in verification key!")
|
||||
.unwrap();
|
||||
let current_line_split: Vec<&str> = current_line.split("=").collect();
|
||||
assert_eq!(current_line_split.len(), 2);
|
||||
let gamma_abc_count: i32 = current_line_split[1].trim().parse().unwrap();
|
||||
|
||||
template_text = vk_gamma_abc_len_regex
|
||||
.replace(
|
||||
template_text.as_str(),
|
||||
format!("{}", gamma_abc_count).as_str(),
|
||||
)
|
||||
.into_owned();
|
||||
template_text = vk_input_len_regex
|
||||
.replace(
|
||||
template_text.as_str(),
|
||||
format!("{}", gamma_abc_count - 1).as_str(),
|
||||
)
|
||||
.into_owned();
|
||||
|
||||
let mut gamma_abc_repeat_text = String::new();
|
||||
for x in 0..gamma_abc_count {
|
||||
let mut curr_template = gamma_abc_template.clone();
|
||||
let current_line: String = lines
|
||||
.next()
|
||||
.expect("Unexpected end of file in verification key!")
|
||||
.unwrap();
|
||||
let current_line_split: Vec<&str> = current_line.split("=").collect();
|
||||
assert_eq!(current_line_split.len(), 2);
|
||||
curr_template = vk_gamma_abc_index_regex
|
||||
.replace(curr_template.as_str(), format!("{}", x).as_str())
|
||||
.into_owned();
|
||||
curr_template = vk_gamma_abc_points_regex
|
||||
.replace(curr_template.as_str(), current_line_split[1].trim())
|
||||
.into_owned();
|
||||
gamma_abc_repeat_text.push_str(curr_template.as_str());
|
||||
if x < gamma_abc_count - 1 {
|
||||
gamma_abc_repeat_text.push_str("\n ");
|
||||
}
|
||||
}
|
||||
template_text = vk_gamma_abc_repeat_regex
|
||||
.replace(template_text.as_str(), gamma_abc_repeat_text.as_str())
|
||||
.into_owned();
|
||||
|
||||
let re = Regex::new(r"(?P<v>0[xX][0-9a-fA-F]{64})").unwrap();
|
||||
template_text = re.replace_all(&template_text, "uint256($v)").to_string();
|
||||
|
||||
format!(
|
||||
"{}{}{}",
|
||||
SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB, template_text
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
mod serialize {
|
||||
|
||||
use bellman::groth16::{Proof, VerifyingKey};
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
|
||||
pub fn serialize_vk(vk: VerifyingKey<Bn256>) -> String {
|
||||
format!(
|
||||
"vk.alpha = {}
|
||||
vk.beta = {}
|
||||
vk.gamma = {}
|
||||
vk.delta = {}
|
||||
vk.gammaABC.len() = {}
|
||||
{}",
|
||||
vk.alpha_g1,
|
||||
vk.beta_g2,
|
||||
vk.gamma_g2,
|
||||
vk.delta_g2,
|
||||
vk.ic.len(),
|
||||
vk.ic
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| format!("vk.gammaABC[{}] = {}", i, x))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
.replace("G2(x=Fq2(Fq(", "[")
|
||||
.replace("), y=Fq(", ", ")
|
||||
.replace("G1(x=Fq(", "")
|
||||
.replace(") + Fq(", ", ")
|
||||
.replace("))", "")
|
||||
.replace(") * u), y=Fq2(Fq(", "], [")
|
||||
.replace(") * u", "]")
|
||||
}
|
||||
|
||||
pub fn serialize_proof(p: &Proof<Bn256>, inputs: &Vec<Fr>) -> String {
|
||||
format!(
|
||||
"{{
|
||||
\"proof\": {{
|
||||
\"a\": {},
|
||||
\"b\": {},
|
||||
\"c\": {}
|
||||
}},
|
||||
\"inputs\": [{}]
|
||||
}}",
|
||||
p.a,
|
||||
p.b,
|
||||
p.c,
|
||||
inputs
|
||||
.iter()
|
||||
.map(|v| format!("\"{}\"", v))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)
|
||||
.replace("G2(x=Fq2(Fq(", "[[\"")
|
||||
.replace("), y=Fq(", "\", \"")
|
||||
.replace("G1(x=Fq(", "[\"")
|
||||
.replace(") + Fq(", "\", \"")
|
||||
.replace(") * u), y=Fq2(Fq(", "\"], [\"")
|
||||
.replace(") * u]", "\"]]")
|
||||
.replace(") * u))", "\"]]")
|
||||
.replace("))", "\"]")
|
||||
.replace("Fr(", "")
|
||||
.replace(")", "")
|
||||
}
|
||||
}
|
||||
|
||||
const CONTRACT_TEMPLATE: &str = r#"
|
||||
contract Verifier {
|
||||
using Pairing for *;
|
||||
struct VerifyingKey {
|
||||
Pairing.G1Point a;
|
||||
Pairing.G2Point b;
|
||||
Pairing.G2Point gamma;
|
||||
Pairing.G2Point delta;
|
||||
Pairing.G1Point[] gammaABC;
|
||||
}
|
||||
struct Proof {
|
||||
Pairing.G1Point A;
|
||||
Pairing.G2Point B;
|
||||
Pairing.G1Point C;
|
||||
}
|
||||
function verifyingKey() pure internal returns (VerifyingKey memory vk) {
|
||||
vk.a = Pairing.G1Point(<%vk_a%>);
|
||||
vk.b = Pairing.G2Point(<%vk_b%>);
|
||||
vk.gamma = Pairing.G2Point(<%vk_gamma%>);
|
||||
vk.delta = Pairing.G2Point(<%vk_delta%>);
|
||||
vk.gammaABC = new Pairing.G1Point[](<%vk_gammaABC_length%>);
|
||||
<%vk_gammaABC_pts%>
|
||||
}
|
||||
function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
|
||||
VerifyingKey memory vk = verifyingKey();
|
||||
require(input.length + 1 == vk.gammaABC.length);
|
||||
// Compute the linear combination vk_x
|
||||
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
|
||||
for (uint i = 0; i < input.length; i++)
|
||||
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gammaABC[i + 1], input[i]));
|
||||
vk_x = Pairing.addition(vk_x, vk.gammaABC[0]);
|
||||
if(!Pairing.pairingProd4(
|
||||
proof.A, proof.B,
|
||||
Pairing.negate(vk_x), vk.gamma,
|
||||
Pairing.negate(proof.C), vk.delta,
|
||||
Pairing.negate(vk.a), vk.b)) return 1;
|
||||
return 0;
|
||||
}
|
||||
event Verified(string s);
|
||||
function verifyTx(
|
||||
uint[2] memory a,
|
||||
uint[2][2] memory b,
|
||||
uint[2] memory c,
|
||||
uint[<%vk_input_length%>] memory input
|
||||
) public returns (bool r) {
|
||||
Proof memory proof;
|
||||
proof.A = Pairing.G1Point(a[0], a[1]);
|
||||
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
|
||||
proof.C = Pairing.G1Point(c[0], c[1]);
|
||||
uint[] memory inputValues = new uint[](input.length);
|
||||
for(uint i = 0; i < input.length; i++){
|
||||
inputValues[i] = input[i];
|
||||
}
|
||||
if (verify(inputValues, proof) == 0) {
|
||||
emit Verified("Transaction successfully verified.");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
mod serialize {
|
||||
use super::*;
|
||||
|
||||
mod proof {
|
||||
use super::*;
|
||||
use flat_absy::FlatVariable;
|
||||
use ir::*;
|
||||
use proof_system::bn128::g16::serialize::serialize_proof;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
struct G16ProofPoints {
|
||||
a: [String; 2],
|
||||
b: [[String; 2]; 2],
|
||||
c: [String; 2],
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
struct G16Proof {
|
||||
proof: G16ProofPoints,
|
||||
inputs: Vec<String>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(0)],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
FlatVariable::new(0).into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![false],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(42)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let public_inputs_values = computation.public_inputs_values();
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let proof = computation.prove(¶ms);
|
||||
|
||||
let serialized_proof = serialize_proof(&proof, &public_inputs_values);
|
||||
serde_json::from_str::<G16Proof>(&serialized_proof).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
extern crate libc;
|
||||
|
||||
use self::libc::{c_char, c_int, uint8_t};
|
||||
use flat_absy::flat_variable::FlatVariable;
|
||||
use proof_system::utils::{
|
||||
prepare_generate_proof, prepare_setup, SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB,
|
||||
};
|
||||
use ir;
|
||||
use proof_system::bn128::utils::libsnark::{prepare_generate_proof, prepare_setup};
|
||||
use proof_system::bn128::utils::solidity::{SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB};
|
||||
use proof_system::ProofSystem;
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
|
@ -46,16 +45,7 @@ extern "C" {
|
|||
}
|
||||
|
||||
impl ProofSystem for GM17 {
|
||||
fn setup(
|
||||
&self,
|
||||
variables: Vec<FlatVariable>,
|
||||
a: Vec<Vec<(usize, FieldPrime)>>,
|
||||
b: Vec<Vec<(usize, FieldPrime)>>,
|
||||
c: Vec<Vec<(usize, FieldPrime)>>,
|
||||
num_inputs: usize,
|
||||
pk_path: &str,
|
||||
vk_path: &str,
|
||||
) -> bool {
|
||||
fn setup(&self, program: ir::Prog<FieldPrime>, pk_path: &str, vk_path: &str) {
|
||||
let (
|
||||
a_arr,
|
||||
b_arr,
|
||||
|
@ -68,7 +58,7 @@ impl ProofSystem for GM17 {
|
|||
num_inputs,
|
||||
pk_path_cstring,
|
||||
vk_path_cstring,
|
||||
) = prepare_setup(variables, a, b, c, num_inputs, pk_path, vk_path);
|
||||
) = prepare_setup(program, pk_path, vk_path);
|
||||
|
||||
unsafe {
|
||||
_gm17_setup(
|
||||
|
@ -83,32 +73,32 @@ impl ProofSystem for GM17 {
|
|||
num_inputs as i32,
|
||||
pk_path_cstring.as_ptr(),
|
||||
vk_path_cstring.as_ptr(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_proof(
|
||||
&self,
|
||||
program: ir::Prog<FieldPrime>,
|
||||
witness: ir::Witness<FieldPrime>,
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
publquery_inputs: Vec<FieldPrime>,
|
||||
private_inputs: Vec<FieldPrime>,
|
||||
) -> bool {
|
||||
let (
|
||||
pk_path_cstring,
|
||||
proof_path_cstring,
|
||||
publquery_inputs_arr,
|
||||
publquery_inputs_length,
|
||||
public_inputs_arr,
|
||||
public_inputs_length,
|
||||
private_inputs_arr,
|
||||
private_inputs_length,
|
||||
) = prepare_generate_proof(pk_path, proof_path, publquery_inputs, private_inputs);
|
||||
) = prepare_generate_proof(program, witness, pk_path, proof_path);
|
||||
|
||||
unsafe {
|
||||
_gm17_generate_proof(
|
||||
pk_path_cstring.as_ptr(),
|
||||
proof_path_cstring.as_ptr(),
|
||||
publquery_inputs_arr[0].as_ptr(),
|
||||
publquery_inputs_length as i32,
|
||||
public_inputs_arr[0].as_ptr(),
|
||||
public_inputs_length as i32,
|
||||
private_inputs_arr[0].as_ptr(),
|
||||
private_inputs_length as i32,
|
||||
)
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
mod g16;
|
||||
#[cfg(feature = "libsnark")]
|
||||
mod gm17;
|
||||
#[cfg(feature = "libsnark")]
|
||||
mod pghr13;
|
||||
|
||||
mod utils;
|
||||
|
||||
pub use self::g16::G16;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub use self::gm17::GM17;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub use self::pghr13::PGHR13;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
extern crate libc;
|
||||
|
||||
use self::libc::{c_char, c_int, uint8_t};
|
||||
use flat_absy::flat_variable::FlatVariable;
|
||||
use proof_system::utils::{
|
||||
prepare_generate_proof, prepare_setup, SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB,
|
||||
};
|
||||
use ir;
|
||||
use proof_system::bn128::utils::libsnark::{prepare_generate_proof, prepare_setup};
|
||||
use proof_system::bn128::utils::solidity::{SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB};
|
||||
use proof_system::ProofSystem;
|
||||
|
||||
use regex::Regex;
|
||||
|
@ -47,16 +46,7 @@ extern "C" {
|
|||
}
|
||||
|
||||
impl ProofSystem for PGHR13 {
|
||||
fn setup(
|
||||
&self,
|
||||
variables: Vec<FlatVariable>,
|
||||
a: Vec<Vec<(usize, FieldPrime)>>,
|
||||
b: Vec<Vec<(usize, FieldPrime)>>,
|
||||
c: Vec<Vec<(usize, FieldPrime)>>,
|
||||
num_inputs: usize,
|
||||
pk_path: &str,
|
||||
vk_path: &str,
|
||||
) -> bool {
|
||||
fn setup(&self, program: ir::Prog<FieldPrime>, pk_path: &str, vk_path: &str) {
|
||||
let (
|
||||
a_arr,
|
||||
b_arr,
|
||||
|
@ -69,7 +59,7 @@ impl ProofSystem for PGHR13 {
|
|||
num_inputs,
|
||||
pk_path_cstring,
|
||||
vk_path_cstring,
|
||||
) = prepare_setup(variables, a, b, c, num_inputs, pk_path, vk_path);
|
||||
) = prepare_setup(program, pk_path, vk_path);
|
||||
|
||||
unsafe {
|
||||
_pghr13_setup(
|
||||
|
@ -84,16 +74,16 @@ impl ProofSystem for PGHR13 {
|
|||
num_inputs as i32,
|
||||
pk_path_cstring.as_ptr(),
|
||||
vk_path_cstring.as_ptr(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_proof(
|
||||
&self,
|
||||
program: ir::Prog<FieldPrime>,
|
||||
witness: ir::Witness<FieldPrime>,
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
public_inputs: Vec<FieldPrime>,
|
||||
private_inputs: Vec<FieldPrime>,
|
||||
) -> bool {
|
||||
let (
|
||||
pk_path_cstring,
|
||||
|
@ -102,7 +92,12 @@ impl ProofSystem for PGHR13 {
|
|||
public_inputs_length,
|
||||
private_inputs_arr,
|
||||
private_inputs_length,
|
||||
) = prepare_generate_proof(pk_path, proof_path, public_inputs, private_inputs);
|
||||
) = prepare_generate_proof(program, witness, pk_path, proof_path);
|
||||
|
||||
println!(
|
||||
"{:?}",
|
||||
(pk_path_cstring.clone(), proof_path_cstring.clone(),)
|
||||
);
|
||||
|
||||
unsafe {
|
||||
_pghr13_generate_proof(
|
||||
|
|
381
zokrates_core/src/proof_system/bn128/utils/bellman.rs
Normal file
381
zokrates_core/src/proof_system/bn128/utils/bellman.rs
Normal file
|
@ -0,0 +1,381 @@
|
|||
extern crate rand;
|
||||
|
||||
use bellman::groth16::Proof;
|
||||
use bellman::groth16::{
|
||||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
|
||||
Parameters,
|
||||
};
|
||||
use bellman::{Circuit, ConstraintSystem, LinearCombination, SynthesisError, Variable};
|
||||
use ir::{LinComb, Prog, Statement, Witness};
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
use std::collections::BTreeMap;
|
||||
use zokrates_field::field::{Field, FieldPrime};
|
||||
|
||||
use self::rand::*;
|
||||
use flat_absy::FlatVariable;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Computation<T: Field> {
|
||||
program: Prog<T>,
|
||||
witness: Option<Witness<T>>,
|
||||
}
|
||||
|
||||
impl<T: Field> Computation<T> {
|
||||
pub fn with_witness(program: Prog<T>, witness: Witness<T>) -> Self {
|
||||
Computation {
|
||||
program,
|
||||
witness: Some(witness),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn without_witness(program: Prog<T>) -> Self {
|
||||
Computation {
|
||||
program,
|
||||
witness: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bellman_combination<CS: ConstraintSystem<Bn256>>(
|
||||
l: LinComb<FieldPrime>,
|
||||
cs: &mut CS,
|
||||
symbols: &mut BTreeMap<FlatVariable, Variable>,
|
||||
witness: &mut Witness<FieldPrime>,
|
||||
) -> LinearCombination<Bn256> {
|
||||
l.0.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
Fr::from(v),
|
||||
symbols
|
||||
.entry(k)
|
||||
.or_insert_with(|| {
|
||||
match k.is_output() {
|
||||
true => cs.alloc_input(
|
||||
|| format!("{}", k),
|
||||
|| {
|
||||
Ok(witness
|
||||
.0
|
||||
.remove(&k)
|
||||
.ok_or(SynthesisError::AssignmentMissing)?
|
||||
.into())
|
||||
},
|
||||
),
|
||||
false => cs.alloc(
|
||||
|| format!("{}", k),
|
||||
|| {
|
||||
Ok(witness
|
||||
.0
|
||||
.remove(&k)
|
||||
.ok_or(SynthesisError::AssignmentMissing)?
|
||||
.into())
|
||||
},
|
||||
),
|
||||
}
|
||||
.unwrap()
|
||||
})
|
||||
.clone(),
|
||||
)
|
||||
})
|
||||
.fold(LinearCombination::zero(), |acc, e| acc + e)
|
||||
}
|
||||
|
||||
impl Prog<FieldPrime> {
|
||||
pub fn synthesize<CS: ConstraintSystem<Bn256>>(
|
||||
self,
|
||||
cs: &mut CS,
|
||||
witness: Option<Witness<FieldPrime>>,
|
||||
) -> Result<(), SynthesisError> {
|
||||
// mapping from IR variables
|
||||
let mut symbols = BTreeMap::new();
|
||||
|
||||
let mut witness = witness.unwrap_or(Witness::empty());
|
||||
|
||||
assert!(symbols.insert(FlatVariable::one(), CS::one()).is_none());
|
||||
|
||||
symbols.extend(
|
||||
self.main
|
||||
.arguments
|
||||
.iter()
|
||||
.zip(self.private)
|
||||
.enumerate()
|
||||
.map(|(index, (var, private))| {
|
||||
let wire = match private {
|
||||
true => cs.alloc(
|
||||
|| format!("PRIVATE_INPUT_{}", index),
|
||||
|| {
|
||||
Ok(witness
|
||||
.0
|
||||
.remove(&var)
|
||||
.ok_or(SynthesisError::AssignmentMissing)?
|
||||
.into())
|
||||
},
|
||||
),
|
||||
false => cs.alloc_input(
|
||||
|| format!("PUBLIC_INPUT_{}", index),
|
||||
|| {
|
||||
Ok(witness
|
||||
.0
|
||||
.remove(&var)
|
||||
.ok_or(SynthesisError::AssignmentMissing)?
|
||||
.into())
|
||||
},
|
||||
),
|
||||
}
|
||||
.unwrap();
|
||||
(var.clone(), wire)
|
||||
}),
|
||||
);
|
||||
|
||||
let main = self.main;
|
||||
|
||||
for statement in main.statements {
|
||||
match statement {
|
||||
Statement::Constraint(quad, lin) => {
|
||||
let a = &bellman_combination(quad.left.clone(), cs, &mut symbols, &mut witness);
|
||||
let b =
|
||||
&bellman_combination(quad.right.clone(), 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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Computation<FieldPrime> {
|
||||
pub fn prove(self, params: &Parameters<Bn256>) -> Proof<Bn256> {
|
||||
let rng = &mut thread_rng();
|
||||
let proof = create_random_proof(self.clone(), params, rng).unwrap();
|
||||
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
// extract public inputs
|
||||
let public_inputs = self.public_inputs_values();
|
||||
|
||||
assert!(verify_proof(&pvk, &proof, &public_inputs).unwrap());
|
||||
|
||||
proof
|
||||
}
|
||||
|
||||
pub fn public_inputs_values(&self) -> Vec<Fr> {
|
||||
self.program
|
||||
.main
|
||||
.arguments
|
||||
.clone()
|
||||
.iter()
|
||||
.zip(self.program.private.clone())
|
||||
.filter(|(_, p)| !p)
|
||||
.map(|(a, _)| a)
|
||||
.map(|v| self.witness.clone().unwrap().0.get(v).unwrap().clone())
|
||||
.chain(self.witness.clone().unwrap().return_values())
|
||||
.map(|v| Fr::from(v.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn setup(self) -> Parameters<Bn256> {
|
||||
let rng = &mut thread_rng();
|
||||
// run setup phase
|
||||
generate_random_parameters(self, rng).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Circuit<Bn256> for Computation<FieldPrime> {
|
||||
fn synthesize<CS: ConstraintSystem<Bn256>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
self.program.synthesize(cs, self.witness)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ir::Function;
|
||||
use zokrates_field::field::FieldPrime;
|
||||
|
||||
mod prove {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![],
|
||||
returns: vec![],
|
||||
statements: vec![],
|
||||
},
|
||||
private: vec![],
|
||||
};
|
||||
|
||||
let witness = program.clone().execute::<FieldPrime>(&vec![]).unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identity() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(0)],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
FlatVariable::new(0).into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![true],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_identity() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(0)],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
FlatVariable::new(0).into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![false],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_arguments() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
FlatVariable::one().into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![],
|
||||
};
|
||||
|
||||
let witness = program.clone().execute::<FieldPrime>(&vec![]).unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unordered_variables() {
|
||||
// public variables must be ordered from 0
|
||||
// private variables can be unordered
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(42), FlatVariable::new(51)],
|
||||
returns: vec![FlatVariable::public(0), FlatVariable::public(1)],
|
||||
statements: vec![
|
||||
Statement::Constraint(
|
||||
(LinComb::from(FlatVariable::new(42))
|
||||
+ LinComb::from(FlatVariable::new(51)))
|
||||
.into(),
|
||||
FlatVariable::public(0).into(),
|
||||
),
|
||||
Statement::Constraint(
|
||||
(LinComb::from(FlatVariable::one())
|
||||
+ LinComb::from(FlatVariable::new(42)))
|
||||
.into(),
|
||||
FlatVariable::public(1).into(),
|
||||
),
|
||||
],
|
||||
},
|
||||
private: vec![true, false],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(3), FieldPrime::from(4)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(42)],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
(LinComb::from(FlatVariable::new(42)) + LinComb::one()).into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![false],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(3)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_directives() {
|
||||
let program: Prog<FieldPrime> = Prog {
|
||||
main: Function {
|
||||
id: String::from("main"),
|
||||
arguments: vec![FlatVariable::new(42), FlatVariable::new(51)],
|
||||
returns: vec![FlatVariable::public(0)],
|
||||
statements: vec![Statement::Constraint(
|
||||
(LinComb::from(FlatVariable::new(42))
|
||||
+ LinComb::from(FlatVariable::new(51)))
|
||||
.into(),
|
||||
FlatVariable::public(0).into(),
|
||||
)],
|
||||
},
|
||||
private: vec![true, false],
|
||||
};
|
||||
|
||||
let witness = program
|
||||
.clone()
|
||||
.execute::<FieldPrime>(&vec![FieldPrime::from(3), FieldPrime::from(4)])
|
||||
.unwrap();
|
||||
let computation = Computation::with_witness(program, witness);
|
||||
|
||||
let params = computation.clone().setup();
|
||||
let _proof = computation.prove(¶ms);
|
||||
}
|
||||
}
|
||||
}
|
306
zokrates_core/src/proof_system/bn128/utils/libsnark.rs
Normal file
306
zokrates_core/src/proof_system/bn128/utils/libsnark.rs
Normal file
|
@ -0,0 +1,306 @@
|
|||
use flat_absy::FlatVariable;
|
||||
use ir::{self, Statement};
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
// utility function. Converts a Fields vector-based byte representation to fixed size array.
|
||||
fn vec_as_u8_32_array(vec: &Vec<u8>) -> [u8; 32] {
|
||||
assert!(vec.len() <= 32);
|
||||
let mut array = [0u8; 32];
|
||||
for (index, byte) in vec.iter().enumerate() {
|
||||
array[31 - index] = *byte;
|
||||
}
|
||||
array
|
||||
}
|
||||
|
||||
// proof-system-independent preparation for the setup phase
|
||||
pub fn prepare_setup<T: Field>(
|
||||
program: ir::Prog<T>,
|
||||
pk_path: &str,
|
||||
vk_path: &str,
|
||||
) -> (
|
||||
Vec<u8>,
|
||||
Vec<u8>,
|
||||
Vec<u8>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
usize,
|
||||
usize,
|
||||
usize,
|
||||
CString,
|
||||
CString,
|
||||
) {
|
||||
// transform to R1CS
|
||||
let (variables, public_variables_count, a, b, c) = r1cs_program(program);
|
||||
|
||||
let num_inputs = public_variables_count - 1;
|
||||
|
||||
let num_constraints = a.len();
|
||||
let num_variables = variables.len();
|
||||
|
||||
// Create single A,B,C vectors of tuples (constraint_number, variable_id, variable_value)
|
||||
let mut a_vec = vec![];
|
||||
let mut b_vec = vec![];
|
||||
let mut c_vec = vec![];
|
||||
for row in 0..num_constraints {
|
||||
for &(idx, ref val) in &a[row] {
|
||||
a_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
for &(idx, ref val) in &b[row] {
|
||||
b_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
for &(idx, ref val) in &c[row] {
|
||||
c_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Sizes and offsets in bytes for our struct {row, id, value}
|
||||
// We're building { i32, i32, i8[32] }
|
||||
const STRUCT_SIZE: usize = 40;
|
||||
|
||||
const ROW_SIZE: usize = 4;
|
||||
|
||||
const IDX_SIZE: usize = 4;
|
||||
const IDX_OFFSET: usize = 4;
|
||||
|
||||
const VALUE_SIZE: usize = 32;
|
||||
const VALUE_OFFSET: usize = 8;
|
||||
|
||||
// Convert above A,B,C vectors to byte arrays for cpp
|
||||
let mut a_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * a_vec.len()];
|
||||
let mut b_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * b_vec.len()];
|
||||
let mut c_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * c_vec.len()];
|
||||
use std::mem::transmute;
|
||||
for (id, (row, idx, val)) in a_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
for (id, (row, idx, val)) in b_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
for (id, (row, idx, val)) in c_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
|
||||
// convert String slices to 'CString's
|
||||
let pk_path_cstring = CString::new(pk_path).unwrap();
|
||||
let vk_path_cstring = CString::new(vk_path).unwrap();
|
||||
|
||||
(
|
||||
a_arr,
|
||||
b_arr,
|
||||
c_arr,
|
||||
a_vec,
|
||||
b_vec,
|
||||
c_vec,
|
||||
num_constraints,
|
||||
num_variables,
|
||||
num_inputs,
|
||||
pk_path_cstring,
|
||||
vk_path_cstring,
|
||||
)
|
||||
}
|
||||
|
||||
// proof-system-independent preparation for proof generation
|
||||
pub fn prepare_generate_proof<T: Field>(
|
||||
program: ir::Prog<T>,
|
||||
witness: ir::Witness<T>,
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
) -> (CString, CString, Vec<[u8; 32]>, usize, Vec<[u8; 32]>, usize) {
|
||||
// recover variable order from the program
|
||||
let (variables, public_variables_count, _, _, _) = r1cs_program(program);
|
||||
|
||||
let witness: Vec<_> = variables.iter().map(|x| witness.0[x].clone()).collect();
|
||||
|
||||
// split witness into public and private inputs at offset
|
||||
let mut public_inputs: Vec<_> = witness.clone();
|
||||
let private_inputs: Vec<_> = public_inputs.split_off(public_variables_count);
|
||||
|
||||
let pk_path_cstring = CString::new(pk_path).unwrap();
|
||||
let proof_path_cstring = CString::new(proof_path).unwrap();
|
||||
|
||||
let public_inputs_length = public_inputs.len();
|
||||
let private_inputs_length = private_inputs.len();
|
||||
|
||||
let mut public_inputs_arr: Vec<[u8; 32]> = vec![[0u8; 32]; public_inputs_length];
|
||||
// length must not be zero here, so we apply the max function
|
||||
let mut private_inputs_arr: Vec<[u8; 32]> = vec![[0u8; 32]; max(private_inputs_length, 1)];
|
||||
|
||||
//convert inputs
|
||||
for (index, value) in public_inputs.into_iter().enumerate() {
|
||||
public_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
|
||||
}
|
||||
for (index, value) in private_inputs.into_iter().enumerate() {
|
||||
private_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
|
||||
}
|
||||
|
||||
(
|
||||
pk_path_cstring,
|
||||
proof_path_cstring,
|
||||
public_inputs_arr,
|
||||
public_inputs_length,
|
||||
private_inputs_arr,
|
||||
private_inputs_length,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the index of `var` in `variables`, adding `var` with incremented index if it not yet exists.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `variables` - A mutual map that maps all existing variables to their index.
|
||||
/// * `var` - Variable to be searched for.
|
||||
pub fn provide_variable_idx(
|
||||
variables: &mut HashMap<FlatVariable, usize>,
|
||||
var: &FlatVariable,
|
||||
) -> usize {
|
||||
let index = variables.len();
|
||||
*variables.entry(*var).or_insert(index)
|
||||
}
|
||||
|
||||
/// Calculates one R1CS row representation of a program and returns (V, A, B, C) so that:
|
||||
/// * `V` contains all used variables and the index in the vector represents the used number in `A`, `B`, `C`
|
||||
/// * `<A,x>*<B,x> = <C,x>` for a witness `x`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `prog` - The program the representation is calculated for.
|
||||
pub fn r1cs_program<T: Field>(
|
||||
prog: ir::Prog<T>,
|
||||
) -> (
|
||||
Vec<FlatVariable>,
|
||||
usize,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
Vec<Vec<(usize, T)>>,
|
||||
) {
|
||||
let mut variables: HashMap<FlatVariable, usize> = HashMap::new();
|
||||
provide_variable_idx(&mut variables, &FlatVariable::one());
|
||||
|
||||
for x in prog
|
||||
.main
|
||||
.arguments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(index, _)| !prog.private[*index])
|
||||
{
|
||||
provide_variable_idx(&mut variables, &x.1);
|
||||
}
|
||||
|
||||
//Only the main function is relevant in this step, since all calls to other functions were resolved during flattening
|
||||
let main = prog.main;
|
||||
|
||||
//~out are added after main's arguments as we want variables (columns)
|
||||
//in the r1cs to be aligned like "public inputs | private inputs"
|
||||
let main_return_count = main.returns.len();
|
||||
|
||||
for i in 0..main_return_count {
|
||||
provide_variable_idx(&mut variables, &FlatVariable::public(i));
|
||||
}
|
||||
|
||||
// position where private part of witness starts
|
||||
let private_inputs_offset = variables.len();
|
||||
|
||||
// first pass through statements to populate `variables`
|
||||
for (quad, lin) in main.statements.iter().filter_map(|s| match s {
|
||||
Statement::Constraint(quad, lin) => Some((quad, lin)),
|
||||
Statement::Directive(..) => None,
|
||||
}) {
|
||||
for (k, _) in &quad.left.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
for (k, _) in &quad.right.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
for (k, _) in &lin.0 {
|
||||
provide_variable_idx(&mut variables, &k);
|
||||
}
|
||||
}
|
||||
|
||||
let mut a = vec![];
|
||||
let mut b = vec![];
|
||||
let mut c = vec![];
|
||||
|
||||
// second pass to convert program to raw sparse vectors
|
||||
for (quad, lin) in main.statements.into_iter().filter_map(|s| match s {
|
||||
Statement::Constraint(quad, lin) => Some((quad, lin)),
|
||||
Statement::Directive(..) => None,
|
||||
}) {
|
||||
a.push(
|
||||
quad.left
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
b.push(
|
||||
quad.right
|
||||
.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
c.push(
|
||||
lin.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (variables.get(&k).unwrap().clone(), v))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
// Convert map back into list ordered by index
|
||||
let mut variables_list = vec![FlatVariable::new(0); variables.len()];
|
||||
for (k, v) in variables.drain() {
|
||||
assert_eq!(variables_list[v], FlatVariable::new(0));
|
||||
std::mem::replace(&mut variables_list[v], k);
|
||||
}
|
||||
(variables_list, private_inputs_offset, a, b, c)
|
||||
}
|
4
zokrates_core/src/proof_system/bn128/utils/mod.rs
Normal file
4
zokrates_core/src/proof_system/bn128/utils/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod bellman;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub mod libsnark;
|
||||
pub mod solidity;
|
|
@ -1,185 +1,3 @@
|
|||
use flat_absy::flat_variable::FlatVariable;
|
||||
use std::cmp::max;
|
||||
use std::ffi::CString;
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
// utility function. Converts a Fields vector-based byte representation to fixed size array.
|
||||
fn vec_as_u8_32_array(vec: &Vec<u8>) -> [u8; 32] {
|
||||
assert!(vec.len() <= 32);
|
||||
let mut array = [0u8; 32];
|
||||
for (index, byte) in vec.iter().enumerate() {
|
||||
array[31 - index] = *byte;
|
||||
}
|
||||
array
|
||||
}
|
||||
|
||||
// proof-system-independent preparation for the setup phase
|
||||
pub fn prepare_setup<T: Field>(
|
||||
variables: Vec<FlatVariable>,
|
||||
a: Vec<Vec<(usize, T)>>,
|
||||
b: Vec<Vec<(usize, T)>>,
|
||||
c: Vec<Vec<(usize, T)>>,
|
||||
num_inputs: usize,
|
||||
pk_path: &str,
|
||||
vk_path: &str,
|
||||
) -> (
|
||||
Vec<u8>,
|
||||
Vec<u8>,
|
||||
Vec<u8>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
Vec<(i32, i32, [u8; 32])>,
|
||||
usize,
|
||||
usize,
|
||||
usize,
|
||||
CString,
|
||||
CString,
|
||||
) {
|
||||
let num_constraints = a.len();
|
||||
let num_variables = variables.len();
|
||||
|
||||
// Create single A,B,C vectors of tuples (constraint_number, variable_id, variable_value)
|
||||
let mut a_vec = vec![];
|
||||
let mut b_vec = vec![];
|
||||
let mut c_vec = vec![];
|
||||
for row in 0..num_constraints {
|
||||
for &(idx, ref val) in &a[row] {
|
||||
a_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
for &(idx, ref val) in &b[row] {
|
||||
b_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
for &(idx, ref val) in &c[row] {
|
||||
c_vec.push((
|
||||
row as i32,
|
||||
idx as i32,
|
||||
vec_as_u8_32_array(&val.into_byte_vector()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Sizes and offsets in bytes for our struct {row, id, value}
|
||||
// We're building { i32, i32, i8[32] }
|
||||
const STRUCT_SIZE: usize = 40;
|
||||
|
||||
const ROW_SIZE: usize = 4;
|
||||
|
||||
const IDX_SIZE: usize = 4;
|
||||
const IDX_OFFSET: usize = 4;
|
||||
|
||||
const VALUE_SIZE: usize = 32;
|
||||
const VALUE_OFFSET: usize = 8;
|
||||
|
||||
// Convert above A,B,C vectors to byte arrays for cpp
|
||||
let mut a_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * a_vec.len()];
|
||||
let mut b_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * b_vec.len()];
|
||||
let mut c_arr: Vec<u8> = vec![0u8; STRUCT_SIZE * c_vec.len()];
|
||||
use std::mem::transmute;
|
||||
for (id, (row, idx, val)) in a_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
a_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
for (id, (row, idx, val)) in b_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
b_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
for (id, (row, idx, val)) in c_vec.iter().enumerate() {
|
||||
let row_bytes: [u8; ROW_SIZE] = unsafe { transmute(row.to_le()) };
|
||||
let idx_bytes: [u8; IDX_SIZE] = unsafe { transmute(idx.to_le()) };
|
||||
|
||||
for x in 0..ROW_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x] = row_bytes[x];
|
||||
}
|
||||
for x in 0..IDX_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x + IDX_OFFSET] = idx_bytes[x];
|
||||
}
|
||||
for x in 0..VALUE_SIZE {
|
||||
c_arr[id * STRUCT_SIZE + x + VALUE_OFFSET] = val[x];
|
||||
}
|
||||
}
|
||||
|
||||
// convert String slices to 'CString's
|
||||
let pk_path_cstring = CString::new(pk_path).unwrap();
|
||||
let vk_path_cstring = CString::new(vk_path).unwrap();
|
||||
|
||||
(
|
||||
a_arr,
|
||||
b_arr,
|
||||
c_arr,
|
||||
a_vec,
|
||||
b_vec,
|
||||
c_vec,
|
||||
num_constraints,
|
||||
num_variables,
|
||||
num_inputs,
|
||||
pk_path_cstring,
|
||||
vk_path_cstring,
|
||||
)
|
||||
}
|
||||
|
||||
// proof-system-independent preparation for proof generation
|
||||
pub fn prepare_generate_proof<T: Field>(
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
public_inputs: Vec<T>,
|
||||
private_inputs: Vec<T>,
|
||||
) -> (CString, CString, Vec<[u8; 32]>, usize, Vec<[u8; 32]>, usize) {
|
||||
let pk_path_cstring = CString::new(pk_path).unwrap();
|
||||
let proof_path_cstring = CString::new(proof_path).unwrap();
|
||||
|
||||
let public_inputs_length = public_inputs.len();
|
||||
let private_inputs_length = private_inputs.len();
|
||||
|
||||
let mut public_inputs_arr: Vec<[u8; 32]> = vec![[0u8; 32]; public_inputs_length];
|
||||
// length must not be zero here, so we apply the max function
|
||||
let mut private_inputs_arr: Vec<[u8; 32]> = vec![[0u8; 32]; max(private_inputs_length, 1)];
|
||||
|
||||
//convert inputs
|
||||
for (index, value) in public_inputs.into_iter().enumerate() {
|
||||
public_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
|
||||
}
|
||||
for (index, value) in private_inputs.into_iter().enumerate() {
|
||||
private_inputs_arr[index] = vec_as_u8_32_array(&value.into_byte_vector());
|
||||
}
|
||||
|
||||
(
|
||||
pk_path_cstring,
|
||||
proof_path_cstring,
|
||||
public_inputs_arr,
|
||||
public_inputs_length,
|
||||
private_inputs_arr,
|
||||
private_inputs_length,
|
||||
)
|
||||
}
|
||||
|
||||
pub const SOLIDITY_G2_ADDITION_LIB: &str = r#"// This file is LGPL3 Licensed
|
||||
|
||||
/**
|
|
@ -1,32 +1,26 @@
|
|||
mod bn128;
|
||||
mod utils;
|
||||
|
||||
use std::fs::File;
|
||||
use zokrates_field::field::FieldPrime;
|
||||
|
||||
pub use self::bn128::G16;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub use self::bn128::GM17;
|
||||
#[cfg(feature = "libsnark")]
|
||||
pub use self::bn128::PGHR13;
|
||||
use flat_absy::flat_variable::FlatVariable;
|
||||
|
||||
use ir;
|
||||
use std::io::BufReader;
|
||||
|
||||
pub trait ProofSystem {
|
||||
fn setup(
|
||||
&self,
|
||||
variables: Vec<FlatVariable>,
|
||||
a: Vec<Vec<(usize, FieldPrime)>>,
|
||||
b: Vec<Vec<(usize, FieldPrime)>>,
|
||||
c: Vec<Vec<(usize, FieldPrime)>>,
|
||||
num_inputs: usize,
|
||||
pk_path: &str,
|
||||
vk_path: &str,
|
||||
) -> bool;
|
||||
fn setup(&self, program: ir::Prog<FieldPrime>, pk_path: &str, vk_path: &str);
|
||||
|
||||
fn generate_proof(
|
||||
&self,
|
||||
program: ir::Prog<FieldPrime>,
|
||||
witness: ir::Witness<FieldPrime>,
|
||||
pk_path: &str,
|
||||
proof_path: &str,
|
||||
public_inputs: Vec<FieldPrime>,
|
||||
private_inputs: Vec<FieldPrime>,
|
||||
) -> bool;
|
||||
|
||||
fn export_solidity_verifier(&self, reader: BufReader<File>) -> String;
|
||||
|
|
|
@ -51,7 +51,7 @@ impl<T: Field> Into<FlatStatement<T>> for Constraint {
|
|||
.into_iter()
|
||||
.map(|(key, val)| {
|
||||
FlatExpression::Mult(
|
||||
box FlatExpression::Number(T::from_dec_string(val.to_string())),
|
||||
box FlatExpression::Number(T::try_from_dec_str(&val).unwrap()),
|
||||
box FlatExpression::Identifier(FlatVariable::new(key)),
|
||||
)
|
||||
})
|
||||
|
@ -69,7 +69,7 @@ impl<T: Field> Into<FlatStatement<T>> for Constraint {
|
|||
.into_iter()
|
||||
.map(|(key, val)| {
|
||||
FlatExpression::Mult(
|
||||
box FlatExpression::Number(T::from_dec_string(val.to_string())),
|
||||
box FlatExpression::Number(T::try_from_dec_str(&val).unwrap()),
|
||||
box FlatExpression::Identifier(FlatVariable::new(key)),
|
||||
)
|
||||
})
|
||||
|
@ -87,7 +87,7 @@ impl<T: Field> Into<FlatStatement<T>> for Constraint {
|
|||
.into_iter()
|
||||
.map(|(key, val)| {
|
||||
FlatExpression::Mult(
|
||||
box FlatExpression::Number(T::from_dec_string(val.to_string())),
|
||||
box FlatExpression::Number(T::try_from_dec_str(&val).unwrap()),
|
||||
box FlatExpression::Identifier(FlatVariable::new(key)),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@ type Val = String;
|
|||
|
||||
impl From<ir::ExecutionResult<FieldPrime>> for ComparableResult {
|
||||
fn from(r: ir::ExecutionResult<FieldPrime>) -> ComparableResult {
|
||||
ComparableResult(r.map(|v| v.return_values().iter().map(|&x| x.clone()).collect()))
|
||||
ComparableResult(r.map(|v| v.return_values()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,8 +44,8 @@ impl From<TestResult> for ComparableResult {
|
|||
fn from(r: TestResult) -> ComparableResult {
|
||||
ComparableResult(r.map(|v| {
|
||||
v.values
|
||||
.into_iter()
|
||||
.map(|v| FieldPrime::from_dec_string(v))
|
||||
.iter()
|
||||
.map(|v| FieldPrime::try_from_dec_str(v).unwrap())
|
||||
.collect()
|
||||
}))
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ macro_rules! zokrates_test {
|
|||
|
||||
for test in t.tests.into_iter() {
|
||||
let input = &test.input.values;
|
||||
let output = bin.execute(&input.iter().map(|v| FieldPrime::from_dec_string(v.clone())).collect());
|
||||
let output = bin.execute(&input.iter().map(|v| FieldPrime::try_from_dec_str(v).unwrap()).collect());
|
||||
|
||||
let context = format!("
|
||||
{}
|
||||
|
|
143
zokrates_field/Cargo.lock
generated
143
zokrates_field/Cargo.lock
generated
|
@ -1,143 +0,0 @@
|
|||
[[package]]
|
||||
name = "bincode"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "zokrates_field"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e103c8b299b28a9c6990458b7013dc4a8356a9b854c51b9883241f5866fac36e"
|
||||
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
|
||||
"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
|
||||
"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
|
||||
"checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
|
||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
|
||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
|
||||
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
|
||||
"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6"
|
||||
"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154"
|
||||
"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
|
@ -12,7 +12,12 @@ bincode = "0.8.0"
|
|||
serde_json = "1.0"
|
||||
num-traits = "0.2"
|
||||
num-integer = "0.1"
|
||||
pairing = { git = "https://github.com/matterinc/pairing", tag = "0.16.2" }
|
||||
ff = { git = 'https://github.com/matterinc/ff', features = ["derive"], tag = "0.5" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.4"
|
||||
|
||||
[dependencies.num-bigint]
|
||||
version = "0.2"
|
||||
features = ["serde"]
|
||||
features = ["serde"]
|
||||
|
|
84
zokrates_field/src/conversion.rs
Normal file
84
zokrates_field/src/conversion.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use crate::field::{Field, FieldPrime};
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bn256::Fr;
|
||||
|
||||
impl From<FieldPrime> for Fr {
|
||||
fn from(e: FieldPrime) -> Fr {
|
||||
let s = e.to_dec_string();
|
||||
Fr::from_str(&s).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Fr> for FieldPrime {
|
||||
fn from(e: Fr) -> FieldPrime {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
e.into_repr().write_le(&mut res).unwrap();
|
||||
FieldPrime::from_byte_vector(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ff::Field;
|
||||
|
||||
extern crate rand;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
#[test]
|
||||
fn fr_to_field_to_fr() {
|
||||
let rng = &mut thread_rng();
|
||||
let a: Fr = rng.gen();
|
||||
|
||||
assert_eq!(Fr::from(FieldPrime::from(a)), a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field_to_fr_to_field() {
|
||||
// use Fr to get a random element
|
||||
let rng = &mut thread_rng();
|
||||
let a: Fr = rng.gen();
|
||||
|
||||
// now test idempotence
|
||||
let a = FieldPrime::from(a);
|
||||
|
||||
assert_eq!(FieldPrime::from(Fr::from(a.clone())), a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one() {
|
||||
let a = FieldPrime::from(1);
|
||||
|
||||
assert_eq!(Fr::from(a), Fr::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero() {
|
||||
let a = FieldPrime::from(0);
|
||||
|
||||
assert_eq!(Fr::from(a), Fr::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minus_one() {
|
||||
let mut a: Fr = Fr::one();
|
||||
a.negate();
|
||||
assert_eq!(FieldPrime::from(a), FieldPrime::from(-1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let mut a: Fr = rng.gen();
|
||||
let b: Fr = rng.gen();
|
||||
|
||||
let aa = FieldPrime::from(a);
|
||||
let bb = FieldPrime::from(b);
|
||||
let cc = aa + bb;
|
||||
|
||||
a.add_assign(&b);
|
||||
|
||||
assert_eq!(FieldPrime::from(a), cc);
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
use lazy_static::lazy_static;
|
||||
use num_bigint::{BigInt, BigUint, Sign, ToBigInt};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{Num, One, Zero};
|
||||
use num_traits::{One, Zero};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
|
@ -59,8 +59,6 @@ pub trait Field:
|
|||
fn from_byte_vector(_: Vec<u8>) -> Self;
|
||||
/// Returns this `Field`'s contents as decimal string
|
||||
fn to_dec_string(&self) -> String;
|
||||
/// Returns an element of this `Field` from a decimal string
|
||||
fn from_dec_string(val: String) -> Self;
|
||||
/// Returns the multiplicative inverse, i.e.: self * self.inverse_mul() = Self::one()
|
||||
fn inverse_mul(&self) -> Self;
|
||||
/// Returns the smallest value that can be represented by this field type.
|
||||
|
@ -70,7 +68,7 @@ pub trait Field:
|
|||
/// Returns the number of required bits to represent this field type.
|
||||
fn get_required_bits() -> usize;
|
||||
/// Tries to parse a string into this representation
|
||||
fn try_from_str<'a>(s: &'a str) -> Result<Self, ()>;
|
||||
fn try_from_dec_str<'a>(s: &'a str) -> Result<Self, ()>;
|
||||
/// Returns a decimal string representing a the member of the equivalence class of this `Field` in Z/pZ
|
||||
/// which lies in [-(p-1)/2, (p-1)/2]
|
||||
fn to_compact_dec_string(&self) -> String;
|
||||
|
@ -100,12 +98,6 @@ impl Field for FieldPrime {
|
|||
self.value.to_str_radix(10)
|
||||
}
|
||||
|
||||
fn from_dec_string(val: String) -> Self {
|
||||
FieldPrime {
|
||||
value: BigInt::from_str_radix(val.as_str(), 10).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn inverse_mul(&self) -> FieldPrime {
|
||||
let (b, s, _) = extended_euclid(&self.value, &*P);
|
||||
assert_eq!(b, BigInt::one());
|
||||
|
@ -126,7 +118,7 @@ impl Field for FieldPrime {
|
|||
fn get_required_bits() -> usize {
|
||||
(*P).bits()
|
||||
}
|
||||
fn try_from_str<'a>(s: &'a str) -> Result<Self, ()> {
|
||||
fn try_from_dec_str<'a>(s: &'a str) -> Result<Self, ()> {
|
||||
let x = BigInt::parse_bytes(s.as_bytes(), 10).ok_or(())?;
|
||||
Ok(FieldPrime {
|
||||
value: &x - x.div_floor(&*P) * &*P,
|
||||
|
@ -364,7 +356,7 @@ mod tests {
|
|||
|
||||
impl<'a> From<&'a str> for FieldPrime {
|
||||
fn from(s: &'a str) -> FieldPrime {
|
||||
FieldPrime::try_from_str(s).unwrap()
|
||||
FieldPrime::try_from_dec_str(s).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -624,7 +616,7 @@ mod tests {
|
|||
fn dec_string_ser_deser() {
|
||||
let fp = FieldPrime::from("101");
|
||||
let bv = fp.to_dec_string();
|
||||
assert_eq!(fp, FieldPrime::from_dec_string(bv));
|
||||
assert_eq!(fp, FieldPrime::try_from_dec_str(&bv).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
mod conversion;
|
||||
pub mod field;
|
||||
|
|
|
@ -27,7 +27,7 @@ fn {test_name}() {{
|
|||
|
||||
for test in t.tests.into_iter() {{
|
||||
let input = &test.input.values;
|
||||
let output = bin.execute(&input.iter().map(|v| FieldPrime::from_dec_string(v.clone())).collect());
|
||||
let output = bin.execute(&input.iter().map(|v| FieldPrime::try_from_dec_str(&v.clone()).unwrap()).collect());
|
||||
|
||||
match utils::compare(output, test.output) {{
|
||||
Err(e) => {{
|
||||
|
|
|
@ -33,7 +33,7 @@ type Val = String;
|
|||
|
||||
impl From<ir::ExecutionResult<FieldPrime>> for ComparableResult {
|
||||
fn from(r: ir::ExecutionResult<FieldPrime>) -> ComparableResult {
|
||||
ComparableResult(r.map(|v| v.return_values().iter().map(|&x| x.clone()).collect()))
|
||||
ComparableResult(r.map(|v| v.return_values()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,8 @@ impl From<TestResult> for ComparableResult {
|
|||
fn from(r: TestResult) -> ComparableResult {
|
||||
ComparableResult(r.map(|v| {
|
||||
v.values
|
||||
.into_iter()
|
||||
.map(|v| FieldPrime::from_dec_string(v))
|
||||
.iter()
|
||||
.map(|v| FieldPrime::try_from_dec_str(v).unwrap())
|
||||
.collect()
|
||||
}))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue