1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

Merge pull request #273 from Zokrates/bellman-backend-v2

Add support for Groth16 with bellman
This commit is contained in:
Thibaut Schaeffer 2019-04-08 22:27:32 +02:00 committed by GitHub
commit 2305cf0684
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1941 additions and 1134 deletions

1
.gitignore vendored
View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

@ -1 +1 @@
[1, 1, 1, 2, 2, 3, 4, 4, 4, 4]
[1, 1, 1, 2, 3, 3, 3, 3]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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());
}
}
}

View file

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

View file

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

View file

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

View 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(&params);
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(&params);
let serialized_proof = serialize_proof(&proof, &public_inputs_values);
serde_json::from_str::<G16Proof>(&serialized_proof).unwrap();
}
}
}
}

View file

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

View file

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

View file

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

View 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(&params.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(&params);
}
#[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(&params);
}
#[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(&params);
}
#[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(&params);
}
#[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(&params);
}
#[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(&params);
}
#[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(&params);
}
}
}

View 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)
}

View file

@ -0,0 +1,4 @@
pub mod bellman;
#[cfg(feature = "libsnark")]
pub mod libsnark;
pub mod solidity;

View file

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

View file

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

View file

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

View file

@ -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!("
{}

View file

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

View file

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

View 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);
}
}

View file

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

View file

@ -1 +1,2 @@
mod conversion;
pub mod field;

View file

@ -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) => {{

View file

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