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

Merge pull request #1041 from Zokrates/transient-ir

Iterator based compilation
This commit is contained in:
Thibaut Schaeffer 2021-12-08 15:41:29 +01:00 committed by GitHub
commit 192cfd1321
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1172 additions and 1238 deletions

23
Cargo.lock generated
View file

@ -1074,6 +1074,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -1845,6 +1851,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
@ -2383,7 +2399,6 @@ name = "zokrates_cli"
version = "0.7.8"
dependencies = [
"assert_cli",
"bincode",
"cfg-if 0.1.10",
"clap",
"dirs",
@ -2393,8 +2408,11 @@ dependencies = [
"lazy_static",
"log",
"regex 0.2.11",
"serde",
"serde_cbor",
"serde_json",
"tempdir",
"typed-arena",
"zokrates_abi",
"zokrates_core",
"zokrates_field",
@ -2421,7 +2439,6 @@ dependencies = [
"ark-relations",
"ark-serialize",
"bellman_ce",
"bincode",
"cc",
"cfg-if 0.1.10",
"cmake",
@ -2441,6 +2458,7 @@ dependencies = [
"reduce",
"regex 0.2.11",
"serde",
"serde_cbor",
"serde_json",
"sha2 0.9.8",
"typed-arena",
@ -2545,6 +2563,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"typed-arena",
"zokrates_abi",
"zokrates_core",
"zokrates_field",

View file

@ -0,0 +1 @@
Reduce compiler memory usage using iterators, change the serialization format to CBOR

View file

@ -31,7 +31,7 @@ Create this file under the name `get_hash.zok`:
Compile the program to a form that is usable for zero knowledge proofs. This command writes
the binary to `get_hash`. You can see a textual representation, somewhat analogous to assembler
coming from a compiler, at `get_hash.ztf` enabled by the `--ztf` command line option.
coming from a compiler, at `get_hash.ztf` created by the `inspect` command.
```
{{#include ../../../zokrates_cli/examples/book/rng_tutorial/test.sh:10}}
```

View file

@ -16,13 +16,15 @@ log = "0.4"
env_logger = "0.9.0"
cfg-if = "0.1"
clap = "2.26.2"
bincode = "0.8.0"
serde_cbor = "0.11.2"
regex = "0.2"
zokrates_field = { version = "0.4", path = "../zokrates_field", default-features = false }
zokrates_abi = { version = "0.1", path = "../zokrates_abi" }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
typed-arena = "1.4.1"
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
dirs = "3.0.1"
lazy_static = "1.4.0"

View file

@ -7,7 +7,7 @@ function zokrates() {
ZOKRATES_STDLIB=$stdlib $bin $*
}
zokrates compile -i get_hash.zok -o get_hash --ztf
zokrates compile -i get_hash.zok -o get_hash && zokrates inspect -i get_hash
zokrates compute-witness --verbose -i get_hash -a 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
mkdir alice bob

View file

@ -1,2 +0,0 @@
def main(private field a) -> field:
return 1

View file

@ -43,6 +43,7 @@ fn cli() -> Result<(), String> {
)
.subcommands(vec![
compile::subcommand(),
inspect::subcommand(),
check::subcommand(),
compute_witness::subcommand(),
#[cfg(feature = "ark")]
@ -60,6 +61,7 @@ fn cli() -> Result<(), String> {
match matches.subcommand() {
("compile", Some(sub_matches)) => compile::exec(sub_matches),
("inspect", Some(sub_matches)) => inspect::exec(sub_matches),
("check", Some(sub_matches)) => check::exec(sub_matches),
("compute-witness", Some(sub_matches)) => compute_witness::exec(sub_matches),
#[cfg(feature = "ark")]
@ -115,6 +117,7 @@ mod tests {
use std::fs::File;
use std::io::{BufReader, Read};
use std::string::String;
use typed_arena::Arena;
use zokrates_core::compile::{compile, CompilationArtifacts, CompileConfig};
use zokrates_core::ir;
use zokrates_field::Bn128Field;
@ -154,11 +157,15 @@ mod tests {
let stdlib = std::fs::canonicalize("../zokrates_stdlib/stdlib").unwrap();
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
let arena = Arena::new();
let res = compile::<Bn128Field, _>(
source,
path,
Some(&resolver),
&CompileConfig::default(),
CompileConfig::default(),
&arena,
);
assert_eq!(res.is_err(), should_error);
}
@ -189,8 +196,16 @@ mod tests {
let stdlib = std::fs::canonicalize("../zokrates_stdlib/stdlib").unwrap();
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
let artifacts: CompilationArtifacts<Bn128Field> =
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();
let arena = Arena::new();
let artifacts: CompilationArtifacts<Bn128Field, _> = compile(
source,
path,
Some(&resolver),
CompileConfig::default(),
&arena,
)
.unwrap();
let interpreter = ir::Interpreter::default();
@ -221,8 +236,16 @@ mod tests {
let stdlib = std::fs::canonicalize("../zokrates_stdlib/stdlib").unwrap();
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
let artifacts: CompilationArtifacts<Bn128Field> =
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();
let arena = Arena::new();
let artifacts: CompilationArtifacts<Bn128Field, _> = compile(
source,
path,
Some(&resolver),
CompileConfig::default(),
&arena,
)
.unwrap();
let interpreter = ir::Interpreter::default();

View file

@ -4,15 +4,16 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use serde_json::to_writer_pretty;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::io::{BufReader, BufWriter, Read};
use std::path::{Path, PathBuf};
use zokrates_core::compile::{compile, CompilationArtifacts, CompileConfig, CompileError};
use typed_arena::Arena;
use zokrates_core::compile::{compile, CompileConfig, CompileError};
use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field};
use zokrates_fs_resolver::FileSystemResolver;
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("compile")
.about("Compiles into flattened conditions. Produces two files: human-readable '.ztf' file for debugging and binary file")
.about("Compiles into a runnable constraint system")
.arg(Arg::with_name("input")
.short("i")
.long("input")
@ -52,24 +53,10 @@ pub fn subcommand() -> App<'static, 'static> {
.required(false)
.possible_values(constants::CURVES)
.default_value(constants::BN128)
).arg(Arg::with_name("allow-unconstrained-variables")
.long("allow-unconstrained-variables")
.help("Allow unconstrained variables by inserting dummy constraints")
.required(false)
).arg(Arg::with_name("isolate-branches")
.long("isolate-branches")
.help("Isolate the execution of branches: a panic in a branch only makes the program panic if this branch is being logically executed")
.required(false)
).arg(Arg::with_name("ztf")
.long("ztf")
.help("Write human readable output (ztf)")
.required(false)
)
.arg(Arg::with_name("light") // TODO: deprecated, should be removed
.long("light")
.required(false)
.overrides_with_all(&["ztf", "verbose"])
.hidden(true)
)
}
@ -84,20 +71,10 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
}
fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
// TODO: remove the warning once light flag is removed entirely
if sub_matches.is_present("light") {
println!(
"Warning: the --light flag is deprecated and will be removed in a coming release.\n\
Terminal output is now off by default and can be activated with the --verbose flag.\n\
Human-readable output file (ztf) is now off by default and can be activated with the --ztf flag.\n"
)
}
println!("Compiling {}\n", sub_matches.value_of("input").unwrap());
let path = PathBuf::from(sub_matches.value_of("input").unwrap());
let bin_output_path = Path::new(sub_matches.value_of("output").unwrap());
let abi_spec_path = Path::new(sub_matches.value_of("abi-spec").unwrap());
let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf");
log::debug!("Load entry point file {}", path.display());
@ -128,16 +105,17 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
)),
}?;
let config = CompileConfig::default()
.allow_unconstrained_variables(sub_matches.is_present("allow-unconstrained-variables"))
.isolate_branches(sub_matches.is_present("isolate-branches"));
let config =
CompileConfig::default().isolate_branches(sub_matches.is_present("isolate-branches"));
let resolver = FileSystemResolver::with_stdlib_root(stdlib_path);
log::debug!("Compile");
let artifacts: CompilationArtifacts<T> = compile(source, path, Some(&resolver), &config)
.map_err(|e| {
let arena = Arena::new();
let artifacts =
compile::<T, _>(source, path, Some(&resolver), config, &arena).map_err(|e| {
format!(
"Compilation failed:\n\n{}",
e.0.iter()
@ -147,10 +125,7 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
)
})?;
let program_flattened = artifacts.prog();
// number of constraints the flattened program will translate to.
let num_constraints = program_flattened.constraint_count();
let (program_flattened, abi) = artifacts.into_inner();
// serialize flattened program and write to binary file
log::debug!("Serialize program");
@ -166,34 +141,9 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
let abi_spec_file = File::create(&abi_spec_path)
.map_err(|why| format!("Could not create {}: {}", abi_spec_path.display(), why))?;
let abi = artifacts.abi();
let mut writer = BufWriter::new(abi_spec_file);
to_writer_pretty(&mut writer, &abi).map_err(|_| "Unable to write data to file.".to_string())?;
if sub_matches.is_present("verbose") {
// debugging output
println!("Compiled program:\n{}", program_flattened);
}
println!("Compiled code written to '{}'", bin_output_path.display());
if sub_matches.is_present("ztf") {
// write human-readable output file
log::debug!("Serialize human readable program");
let hr_output_file = File::create(&hr_output_path)
.map_err(|why| format!("Could not create {}: {}", hr_output_path.display(), why))?;
let mut hrofb = BufWriter::new(hr_output_file);
writeln!(&mut hrofb, "{}", program_flattened)
.map_err(|_| "Unable to write data to file".to_string())?;
hrofb
.flush()
.map_err(|_| "Unable to flush buffer".to_string())?;
println!("Human readable code to '{}'", hr_output_path.display());
}
println!("Number of constraints: {}", num_constraints);
Ok(())
}

View file

@ -76,16 +76,13 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
}
}
fn cli_compute<T: Field>(ir_prog: ir::Prog<T>, sub_matches: &ArgMatches) -> Result<(), String> {
fn cli_compute<T: Field, I: Iterator<Item = ir::Statement<T>>>(
ir_prog: ir::ProgIterator<T, I>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
println!("Computing witness...");
let verbose = sub_matches.is_present("verbose");
// print deserialized flattened program if in verbose mode
if verbose {
println!("{}", ir_prog);
}
let is_stdin = sub_matches.is_present("stdin");
let is_abi = sub_matches.is_present("abi");
@ -106,7 +103,7 @@ fn cli_compute<T: Field>(ir_prog: ir::Prog<T>, sub_matches: &ArgMatches) -> Resu
}
false => ConcreteSignature::new()
.inputs(vec![ConcreteType::FieldElement; ir_prog.arguments.len()])
.outputs(vec![ConcreteType::FieldElement; ir_prog.returns.len()]),
.outputs(vec![ConcreteType::FieldElement; ir_prog.return_count]),
};
use zokrates_abi::Inputs;
@ -140,7 +137,7 @@ fn cli_compute<T: Field>(ir_prog: ir::Prog<T>, sub_matches: &ArgMatches) -> Resu
}
Err(_) => Err(String::from("???")),
},
false => match ir_prog.arguments_count() {
false => match ir_prog.arguments.len() {
0 => Ok(Inputs::Raw(vec![])),
_ => match stdin.read_to_string(&mut input) {
Ok(_) => {
@ -162,7 +159,7 @@ fn cli_compute<T: Field>(ir_prog: ir::Prog<T>, sub_matches: &ArgMatches) -> Resu
let interpreter = ir::Interpreter::default();
let witness = interpreter
.execute(&ir_prog, &arguments.encode())
.execute(ir_prog, &arguments.encode())
.map_err(|e| format!("Execution failed: {}", e))?;
use zokrates_abi::Decode;

View file

@ -105,29 +105,33 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
match parameters {
#[cfg(feature = "bellman")]
Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog {
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, G16, Bellman>(p, sub_matches),
ProgEnum::Bls12_381Program(p) => cli_generate_proof::<_, G16, Bellman>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, _, G16, Bellman>(p, sub_matches),
ProgEnum::Bls12_381Program(p) => {
cli_generate_proof::<_, _, G16, Bellman>(p, sub_matches)
}
_ => unreachable!(),
},
#[cfg(feature = "ark")]
Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog {
ProgEnum::Bls12_377Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches),
ProgEnum::Bw6_761Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, GM17, Ark>(p, sub_matches),
ProgEnum::Bls12_377Program(p) => cli_generate_proof::<_, _, GM17, Ark>(p, sub_matches),
ProgEnum::Bw6_761Program(p) => cli_generate_proof::<_, _, GM17, Ark>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, _, GM17, Ark>(p, sub_matches),
_ => unreachable!(),
},
#[cfg(feature = "ark")]
Parameters(BackendParameter::Ark, _, SchemeParameter::MARLIN) => match prog {
ProgEnum::Bls12_377Program(p) => cli_generate_proof::<_, Marlin, Ark>(p, sub_matches),
ProgEnum::Bw6_761Program(p) => cli_generate_proof::<_, Marlin, Ark>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, Marlin, Ark>(p, sub_matches),
ProgEnum::Bls12_377Program(p) => {
cli_generate_proof::<_, _, Marlin, Ark>(p, sub_matches)
}
ProgEnum::Bw6_761Program(p) => cli_generate_proof::<_, _, Marlin, Ark>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_generate_proof::<_, _, Marlin, Ark>(p, sub_matches),
_ => unreachable!(),
},
#[cfg(feature = "libsnark")]
Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::GM17) => {
match prog {
ProgEnum::Bn128Program(p) => {
cli_generate_proof::<_, GM17, Libsnark>(p, sub_matches)
cli_generate_proof::<_, _, GM17, Libsnark>(p, sub_matches)
}
_ => unreachable!(),
}
@ -136,7 +140,7 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::PGHR13) => {
match prog {
ProgEnum::Bn128Program(p) => {
cli_generate_proof::<_, PGHR13, Libsnark>(p, sub_matches)
cli_generate_proof::<_, _, PGHR13, Libsnark>(p, sub_matches)
}
_ => unreachable!(),
}
@ -145,8 +149,13 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
}
}
fn cli_generate_proof<T: Field, S: Scheme<T>, B: Backend<T, S>>(
program: ir::Prog<T>,
fn cli_generate_proof<
T: Field,
I: Iterator<Item = ir::Statement<T>>,
S: Scheme<T>,
B: Backend<T, S>,
>(
program: ir::ProgIterator<T, I>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
println!("Generating proof...");

View file

@ -49,12 +49,17 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
}
}
fn cli_smtlib2<T: Field>(ir_prog: ir::Prog<T>, sub_matches: &ArgMatches) -> Result<(), String> {
fn cli_smtlib2<T: Field, I: Iterator<Item = ir::Statement<T>>>(
ir_prog: ir::ProgIterator<T, I>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
println!("Generating SMTLib2...");
let output_path = Path::new(sub_matches.value_of("output").unwrap());
let mut output_file = File::create(output_path).unwrap();
let ir_prog = ir_prog.collect();
output_file
.write(format!("{}", SMTLib2Display(&ir_prog)).as_bytes())
.map_err(|why| format!("Could not save smtlib2: {:?}", why))?;

View file

@ -0,0 +1,56 @@
use crate::constants::FLATTENED_CODE_DEFAULT_PATH;
use clap::{App, Arg, ArgMatches, SubCommand};
use std::fs::File;
use std::io::{BufReader, Write};
use std::path::{Path, PathBuf};
use zokrates_core::ir;
use zokrates_core::ir::ProgEnum;
use zokrates_field::Field;
pub fn subcommand() -> App<'static, 'static> {
SubCommand::with_name("inspect")
.about("Outputs a compiled program to a file in a human readable format")
.arg(
Arg::with_name("input")
.short("i")
.long("input")
.help("Path of the binary")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(FLATTENED_CODE_DEFAULT_PATH),
)
}
pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
// read compiled program
let path = Path::new(sub_matches.value_of("input").unwrap());
let file =
File::open(&path).map_err(|why| format!("Could not open {}: {}", path.display(), why))?;
let mut reader = BufReader::new(file);
match ProgEnum::deserialize(&mut reader)? {
ProgEnum::Bn128Program(p) => cli_inspect(p, sub_matches),
ProgEnum::Bls12_377Program(p) => cli_inspect(p, sub_matches),
ProgEnum::Bls12_381Program(p) => cli_inspect(p, sub_matches),
ProgEnum::Bw6_761Program(p) => cli_inspect(p, sub_matches),
}
}
fn cli_inspect<T: Field, I: Iterator<Item = ir::Statement<T>>>(
ir_prog: ir::ProgIterator<T, I>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
let output_path = PathBuf::from(sub_matches.value_of("input").unwrap()).with_extension("ztf");
let mut output_file = File::create(&output_path).unwrap();
let ir_prog: ir::Prog<T> = ir_prog.collect();
output_file
.write(format!("{}", ir_prog).as_bytes())
.map_err(|why| format!("Could not save ztf: {:?}", why))?;
println!("ztf file written to '{}'", output_path.display());
Ok(())
}

View file

@ -5,6 +5,7 @@ pub mod export_verifier;
#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))]
pub mod generate_proof;
pub mod generate_smtlib2;
pub mod inspect;
pub mod print_proof;
#[cfg(any(feature = "bellman", feature = "ark", feature = "libsnark"))]
pub mod setup;

View file

@ -105,19 +105,23 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
match parameters {
#[cfg(feature = "bellman")]
Parameters(BackendParameter::Bellman, _, SchemeParameter::G16) => match prog {
ProgEnum::Bn128Program(p) => cli_setup_non_universal::<_, G16, Bellman>(p, sub_matches),
ProgEnum::Bn128Program(p) => {
cli_setup_non_universal::<_, _, G16, Bellman>(p, sub_matches)
}
ProgEnum::Bls12_381Program(p) => {
cli_setup_non_universal::<_, G16, Bellman>(p, sub_matches)
cli_setup_non_universal::<_, _, G16, Bellman>(p, sub_matches)
}
_ => unreachable!(),
},
#[cfg(feature = "ark")]
Parameters(BackendParameter::Ark, _, SchemeParameter::GM17) => match prog {
ProgEnum::Bls12_377Program(p) => {
cli_setup_non_universal::<_, GM17, Ark>(p, sub_matches)
cli_setup_non_universal::<_, _, GM17, Ark>(p, sub_matches)
}
ProgEnum::Bw6_761Program(p) => cli_setup_non_universal::<_, GM17, Ark>(p, sub_matches),
ProgEnum::Bn128Program(p) => cli_setup_non_universal::<_, GM17, Ark>(p, sub_matches),
ProgEnum::Bw6_761Program(p) => {
cli_setup_non_universal::<_, _, GM17, Ark>(p, sub_matches)
}
ProgEnum::Bn128Program(p) => cli_setup_non_universal::<_, _, GM17, Ark>(p, sub_matches),
_ => unreachable!(),
},
#[cfg(feature = "ark")]
@ -137,13 +141,13 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
match prog {
ProgEnum::Bls12_377Program(p) => {
cli_setup_universal::<_, Marlin, Ark>(p, setup, sub_matches)
cli_setup_universal::<_, _, Marlin, Ark>(p, setup, sub_matches)
}
ProgEnum::Bn128Program(p) => {
cli_setup_universal::<_, Marlin, Ark>(p, setup, sub_matches)
cli_setup_universal::<_, _, Marlin, Ark>(p, setup, sub_matches)
}
ProgEnum::Bw6_761Program(p) => {
cli_setup_universal::<_, Marlin, Ark>(p, setup, sub_matches)
cli_setup_universal::<_, _, Marlin, Ark>(p, setup, sub_matches)
}
_ => unreachable!(),
}
@ -152,7 +156,7 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::GM17) => {
match prog {
ProgEnum::Bn128Program(p) => {
cli_setup_non_universal::<_, GM17, Libsnark>(p, sub_matches)
cli_setup_non_universal::<_, _, GM17, Libsnark>(p, sub_matches)
}
_ => unreachable!(),
}
@ -161,7 +165,7 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
Parameters(BackendParameter::Libsnark, CurveParameter::Bn128, SchemeParameter::PGHR13) => {
match prog {
ProgEnum::Bn128Program(p) => {
cli_setup_non_universal::<_, PGHR13, Libsnark>(p, sub_matches)
cli_setup_non_universal::<_, _, PGHR13, Libsnark>(p, sub_matches)
}
_ => unreachable!(),
}
@ -170,17 +174,17 @@ pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
}
}
fn cli_setup_non_universal<T: Field, S: NonUniversalScheme<T>, B: NonUniversalBackend<T, S>>(
program: ir::Prog<T>,
fn cli_setup_non_universal<
T: Field,
I: Iterator<Item = ir::Statement<T>>,
S: NonUniversalScheme<T>,
B: NonUniversalBackend<T, S>,
>(
program: ir::ProgIterator<T, I>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
println!("Performing setup...");
// print deserialized flattened program if in verbose mode
if sub_matches.is_present("verbose") {
println!("{}", program);
}
// get paths for proving and verification keys
let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap());
let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap());
@ -214,18 +218,18 @@ fn cli_setup_non_universal<T: Field, S: NonUniversalScheme<T>, B: NonUniversalBa
Ok(())
}
fn cli_setup_universal<T: Field, S: UniversalScheme<T>, B: UniversalBackend<T, S>>(
program: ir::Prog<T>,
fn cli_setup_universal<
T: Field,
I: Iterator<Item = ir::Statement<T>>,
S: UniversalScheme<T>,
B: UniversalBackend<T, S>,
>(
program: ir::ProgIterator<T, I>,
srs: Vec<u8>,
sub_matches: &ArgMatches,
) -> Result<(), String> {
println!("Performing setup...");
// print deserialized flattened program if in verbose mode
if sub_matches.is_present("verbose") {
println!("{}", program);
}
// get paths for proving and verification keys
let pk_path = Path::new(sub_matches.value_of("proving-key-path").unwrap());
let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap());

View file

@ -26,7 +26,7 @@ reduce = "0.1.1"
# serialization and deserialization
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
bincode = "0.8.0"
serde_cbor = "0.11.2"
hex = "0.4.2"
regex = "0.2"
zokrates_field = { version = "0.4.0", path = "../zokrates_field", default-features = false }

View file

@ -4,13 +4,12 @@
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
//! @date 2018
use crate::absy::{Module, OwnedModuleId, Program};
use crate::flatten::Flattener;
use crate::flatten::FlattenerIterator;
use crate::imports::{self, Importer};
use crate::ir;
use crate::macros;
use crate::semantics::{self, Checker};
use crate::static_analysis;
use crate::static_analysis::Analyse;
use crate::typed_absy::abi::Abi;
use crate::zir::ZirProgram;
use macros::process_macros;
@ -25,19 +24,30 @@ use zokrates_field::Field;
use zokrates_pest_ast as pest;
#[derive(Debug)]
pub struct CompilationArtifacts<T: Field> {
prog: ir::Prog<T>,
pub struct CompilationArtifacts<T, I: IntoIterator<Item = ir::Statement<T>>> {
prog: ir::ProgIterator<T, I>,
abi: Abi,
}
impl<T: Field> CompilationArtifacts<T> {
pub fn prog(&self) -> &ir::Prog<T> {
&self.prog
impl<T, I: IntoIterator<Item = ir::Statement<T>>> CompilationArtifacts<T, I> {
pub fn prog(self) -> ir::ProgIterator<T, I> {
self.prog
}
pub fn abi(&self) -> &Abi {
&self.abi
}
pub fn into_inner(self) -> (ir::ProgIterator<T, I>, Abi) {
(self.prog, self.abi)
}
pub fn collect(self) -> CompilationArtifacts<T, Vec<ir::Statement<T>>> {
CompilationArtifacts {
prog: self.prog.collect(),
abi: self.abi,
}
}
}
#[derive(Debug)]
@ -162,19 +172,13 @@ impl fmt::Display for CompileErrorInner {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)]
pub struct CompileConfig {
#[serde(default)]
pub allow_unconstrained_variables: bool,
#[serde(default)]
pub isolate_branches: bool,
}
impl CompileConfig {
pub fn allow_unconstrained_variables(mut self, flag: bool) -> Self {
self.allow_unconstrained_variables = flag;
self
}
pub fn isolate_branches(mut self, flag: bool) -> Self {
self.isolate_branches = flag;
self
@ -183,41 +187,29 @@ impl CompileConfig {
type FilePath = PathBuf;
pub fn compile<T: Field, E: Into<imports::Error>>(
pub fn compile<'ast, T: Field, E: Into<imports::Error>>(
source: String,
location: FilePath,
resolver: Option<&dyn Resolver<E>>,
config: &CompileConfig,
) -> Result<CompilationArtifacts<T>, CompileErrors> {
let arena = Arena::new();
let (typed_ast, abi) = check_with_arena(source, location.clone(), resolver, config, &arena)?;
config: CompileConfig,
arena: &'ast Arena<String>,
) -> Result<CompilationArtifacts<T, impl IntoIterator<Item = ir::Statement<T>> + 'ast>, CompileErrors>
{
let (typed_ast, abi): (crate::zir::ZirProgram<'_, T>, _) =
check_with_arena(source, location, resolver, &config, arena)?;
// flatten input program
log::debug!("Flatten");
let program_flattened = Flattener::flatten(typed_ast, config);
log::trace!("\n{}", program_flattened);
// constant propagation after call resolution
log::debug!("Propagate flat program");
let program_flattened = program_flattened.propagate();
log::trace!("\n{}", program_flattened);
let program_flattened = FlattenerIterator::from_function_and_config(typed_ast.main, config);
// convert to ir
log::debug!("Convert to IR");
let ir_prog = ir::Prog::from(program_flattened);
log::trace!("\n{}", ir_prog);
let ir_prog = ir::from_flat::from_flat(program_flattened);
// optimize
log::debug!("Optimise IR");
let optimized_ir_prog = ir_prog.optimize();
// analyse ir (check constraints)
log::debug!("Analyse IR");
let optimized_ir_prog = optimized_ir_prog
.analyse()
.map_err(|e| CompileErrorInner::from(e).in_file(location.as_path()))?;
Ok(CompilationArtifacts {
prog: optimized_ir_prog,
abi,
@ -329,13 +321,18 @@ mod test {
return foo()
"#
.to_string();
let res: Result<CompilationArtifacts<Bn128Field>, CompileErrors> = compile(
let arena = Arena::new();
let res: Result<CompilationArtifacts<Bn128Field, _>, CompileErrors> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
&CompileConfig::default(),
);
assert!(res.unwrap_err().0[0]
CompileConfig::default(),
&arena,
)
.map(|res| res.collect());
let e = res.unwrap_err();
assert!(e.0[0]
.value()
.to_string()
.contains(&"Cannot resolve import without a resolver"));
@ -348,11 +345,15 @@ mod test {
return 1
"#
.to_string();
let res: Result<CompilationArtifacts<Bn128Field>, CompileErrors> = compile(
let arena = Arena::new();
let res: Result<CompilationArtifacts<Bn128Field, _>, CompileErrors> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
&CompileConfig::default(),
CompileConfig::default(),
&arena,
);
assert!(res.is_ok());
}
@ -429,11 +430,14 @@ struct Bar { field a }
}
}
let arena = Arena::new();
let artifacts = compile::<Bn128Field, io::Error>(
main.to_string(),
"main".into(),
Some(&CustomResolver),
&CompileConfig::default(),
CompileConfig::default(),
&arena,
)
.unwrap();

View file

@ -3,7 +3,7 @@ use crate::absy::{
ConstantGenericNode, Expression,
};
use crate::flat_absy::{
FlatDirective, FlatExpression, FlatExpressionList, FlatFunction, FlatParameter, FlatStatement,
FlatDirective, FlatExpression, FlatFunctionIterator, FlatParameter, FlatStatement,
FlatVariable, RuntimeError,
};
use crate::solvers::Solver;
@ -312,18 +312,6 @@ impl FlatEmbed {
FlatEmbed::SnarkVerifyBls12377 => "_SNARK_VERIFY_BLS12_377",
}
}
/// Actually get the `FlatFunction` that this `FlatEmbed` represents
pub fn synthetize<T: Field>(&self, generics: &[u32]) -> FlatFunction<T> {
match self {
FlatEmbed::Unpack => unpack_to_bitwidth(generics[0] as usize),
#[cfg(feature = "bellman")]
FlatEmbed::Sha256Round => sha256_round(),
#[cfg(feature = "ark")]
FlatEmbed::SnarkVerifyBls12377 => snark_verify_bls12_377(generics[0] as usize),
_ => unreachable!(),
}
}
}
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
@ -357,13 +345,17 @@ fn flat_expression_from_vec<T: Field>(v: &[(usize, T)]) -> FlatExpression<T> {
/// - constraint system variables
/// - arguments
#[cfg(feature = "bellman")]
pub fn sha256_round<T: Field>() -> FlatFunction<T> {
pub fn sha256_round<T: Field>(
) -> FlatFunctionIterator<T, impl IntoIterator<Item = FlatStatement<T>>> {
use zokrates_field::Bn128Field;
assert_eq!(T::id(), Bn128Field::id());
// Define iterators for all indices at hand
let (r1cs, input_indices, current_hash_indices, output_indices) =
generate_sha256_round_constraints::<Bn256>();
// The output count
let return_count = output_indices.len();
// indices of the input
let input_indices = input_indices.into_iter();
// indices of the current hash
@ -375,18 +367,21 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
let cs_indices = 0..variable_count;
// indices of the arguments to the function
// apply an offset of `variable_count` to get the indice of our dummy `input` argument
let input_argument_indices = input_indices
let input_argument_indices: Vec<_> = input_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
.map(|i| i + variable_count)
.collect();
// apply an offset of `variable_count` to get the indice of our dummy `current_hash` argument
let current_hash_argument_indices = current_hash_indices
let current_hash_argument_indices: Vec<_> = current_hash_indices
.clone()
.into_iter()
.map(|i| i + variable_count);
.map(|i| i + variable_count)
.collect();
// define parameters to the function based on the variables
let arguments = input_argument_indices
.clone()
.into_iter()
.chain(current_hash_argument_indices.clone())
.map(|i| FlatParameter {
id: FlatVariable::new(i),
@ -401,7 +396,7 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
);
let input_binding_statements =
// bind input and current_hash to inputs
input_indices.chain(current_hash_indices).zip(input_argument_indices.clone().chain(current_hash_argument_indices.clone())).map(|(cs_index, argument_index)| {
input_indices.chain(current_hash_indices).zip(input_argument_indices.clone().into_iter().chain(current_hash_argument_indices.clone())).map(|(cs_index, argument_index)| {
FlatStatement::Condition(
FlatVariable::new(cs_index).into(),
FlatVariable::new(argument_index).into(),
@ -423,36 +418,39 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
});
// define which subset of the witness is returned
let outputs: Vec<FlatExpression<T>> = output_indices
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
.collect();
let outputs = output_indices.map(|o| FlatExpression::Identifier(FlatVariable::new(o)));
// insert a directive to set the witness based on the bellman gadget and inputs
let directive_statement = FlatStatement::Directive(FlatDirective {
outputs: cs_indices.map(FlatVariable::new).collect(),
inputs: input_argument_indices
.into_iter()
.chain(current_hash_argument_indices)
.map(|i| FlatVariable::new(i).into())
.collect(),
solver: Solver::Sha256Round,
});
// insert a statement to return the subset of the witness
let return_statement = FlatStatement::Return(FlatExpressionList {
expressions: outputs,
});
let return_statements = outputs
.into_iter()
.enumerate()
.map(|(index, e)| FlatStatement::Definition(FlatVariable::public(index), e));
let statements = std::iter::once(directive_statement)
.chain(std::iter::once(one_binding_statement))
.chain(input_binding_statements)
.chain(constraint_statements)
.chain(std::iter::once(return_statement))
.collect();
FlatFunction {
.chain(return_statements);
FlatFunctionIterator {
arguments,
statements,
return_count,
}
}
#[cfg(feature = "ark")]
pub fn snark_verify_bls12_377<T: Field>(n: usize) -> FlatFunction<T> {
pub fn snark_verify_bls12_377<T: Field>(
n: usize,
) -> FlatFunctionIterator<T, impl IntoIterator<Item = FlatStatement<T>>> {
use zokrates_field::Bw6_761Field;
assert_eq!(T::id(), Bw6_761Field::id());
@ -528,9 +526,10 @@ pub fn snark_verify_bls12_377<T: Field>(n: usize) -> FlatFunction<T> {
})
.collect();
let return_statement = FlatStatement::Return(FlatExpressionList {
expressions: vec![FlatExpression::Identifier(FlatVariable::new(out_index))],
});
let return_statement = FlatStatement::Definition(
FlatVariable::public(0),
FlatExpression::Identifier(FlatVariable::new(out_index)),
);
// insert a directive to set the witness
let directive_statement = FlatStatement::Directive(FlatDirective {
@ -543,16 +542,16 @@ pub fn snark_verify_bls12_377<T: Field>(n: usize) -> FlatFunction<T> {
solver: Solver::SnarkVerifyBls12377(n),
});
let statements: Vec<_> = std::iter::once(directive_statement)
let statements = std::iter::once(directive_statement)
.chain(std::iter::once(one_binding_statement))
.chain(input_binding_statements)
.chain(constraint_statements)
.chain(std::iter::once(return_statement))
.collect();
.chain(std::iter::once(return_statement));
FlatFunction {
FlatFunctionIterator {
arguments,
statements,
return_count: 1,
}
}
@ -575,7 +574,9 @@ fn use_variable(
/// # Remarks
/// * the return value of the `FlatFunction` is not deterministic if `bit_width >= T::get_required_bits()`
/// as some elements can have multiple representations: For example, `unpack(0)` is `[0, ..., 0]` but also `unpack(p)`
pub fn unpack_to_bitwidth<T: Field>(bit_width: usize) -> FlatFunction<T> {
pub fn unpack_to_bitwidth<T: Field>(
bit_width: usize,
) -> FlatFunctionIterator<T, impl IntoIterator<Item = FlatStatement<T>>> {
let mut counter = 0;
let mut layout = HashMap::new();
@ -599,11 +600,12 @@ pub fn unpack_to_bitwidth<T: Field>(bit_width: usize) -> FlatFunction<T> {
let solver = Solver::bits(bit_width);
let outputs = directive_outputs
#[allow(clippy::needless_collect)]
let outputs: Vec<_> = directive_outputs
.iter()
.enumerate()
.map(|(_, o)| FlatExpression::Identifier(*o))
.collect::<Vec<_>>();
.collect();
// o253, o252, ... o{253 - (bit_width - 1)} are bits
let mut statements: Vec<FlatStatement<T>> = (0..bit_width)
@ -648,13 +650,17 @@ pub fn unpack_to_bitwidth<T: Field>(bit_width: usize) -> FlatFunction<T> {
}),
);
statements.push(FlatStatement::Return(FlatExpressionList {
expressions: outputs,
}));
statements.extend(
outputs
.into_iter()
.enumerate()
.map(|(index, e)| FlatStatement::Definition(FlatVariable::public(index), e)),
);
FlatFunction {
FlatFunctionIterator {
arguments,
statements,
statements: statements.into_iter(),
return_count: bit_width,
}
}
@ -669,17 +675,13 @@ mod tests {
#[test]
fn split254() {
let unpack: FlatFunction<Bn128Field> =
unpack_to_bitwidth(Bn128Field::get_required_bits());
let unpack =
unpack_to_bitwidth::<Bn128Field>(Bn128Field::get_required_bits()).collect();
assert_eq!(
unpack.arguments,
vec![FlatParameter::private(FlatVariable::new(0))]
);
assert_eq!(
unpack.statements.len(),
Bn128Field::get_required_bits() + 1 + 1 + 1
); // 128 bit checks, 1 directive, 1 sum check, 1 return
assert_eq!(
unpack.statements[0],
FlatStatement::Directive(FlatDirective::new(
@ -691,13 +693,9 @@ mod tests {
))
);
assert_eq!(
*unpack.statements.last().unwrap(),
FlatStatement::Return(FlatExpressionList {
expressions: (0..Bn128Field::get_required_bits())
.map(|i| FlatExpression::Identifier(FlatVariable::new(i + 1)))
.collect()
})
);
unpack.statements.len(),
Bn128Field::get_required_bits() + 1 + 1 + Bn128Field::get_required_bits()
) // 254 bit checks, 1 directive, 1 sum check, 254 returns
}
}
@ -711,24 +709,13 @@ mod tests {
fn generate_sha256_constraints() {
let compiled = sha256_round::<Bn128Field>();
let compiled = compiled.collect();
// function should have 768 inputs
assert_eq!(compiled.arguments.len(), 768);
// function should return 256 values
assert_eq!(
compiled
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Return(v) => Some(v),
_ => None,
})
.next()
.unwrap()
.expressions
.len(),
256,
);
assert_eq!(compiled.return_count, 256,);
// directive should take 768 inputs and return n_var outputs
let directive = compiled
@ -768,18 +755,16 @@ mod tests {
)
);
let flat_prog = crate::flat_absy::FlatProg { main: compiled };
let prog = crate::ir::Prog::from(flat_prog);
let input: Vec<_> = (0..512)
.map(|_| 0)
.chain((0..256).map(|_| 1))
.map(Bn128Field::from)
.collect();
let ir = crate::ir::from_flat::from_flat(compiled);
let interpreter = Interpreter::default();
interpreter.execute(&prog, &input).unwrap();
interpreter.execute(ir, &input).unwrap();
}
}
}

View file

@ -98,42 +98,43 @@ impl fmt::Display for RuntimeError {
}
}
#[derive(Clone, PartialEq)]
pub struct FlatProg<T: Field> {
/// FlatFunctions of the program
pub main: FlatFunction<T>,
}
pub type FlatProg<T> = FlatFunction<T>;
impl<T: Field> fmt::Display for FlatProg<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.main)
}
}
pub type FlatFunction<T> = FlatFunctionIterator<T, Vec<FlatStatement<T>>>;
impl<T: Field> fmt::Debug for FlatProg<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "flat_program(main: {}\t)", self.main)
}
}
pub type FlatProgIterator<T, I> = FlatFunctionIterator<T, I>;
#[derive(Clone, PartialEq)]
pub struct FlatFunction<T: Field> {
#[derive(Clone, PartialEq, Debug)]
pub struct FlatFunctionIterator<T, I: IntoIterator<Item = FlatStatement<T>>> {
/// Arguments of the function
pub arguments: Vec<FlatParameter>,
/// Vector of statements that are executed when running the function
pub statements: Vec<FlatStatement<T>>,
pub statements: I,
/// Number of outputs
pub return_count: usize,
}
impl<T, I: IntoIterator<Item = FlatStatement<T>>> FlatFunctionIterator<T, I> {
pub fn collect(self) -> FlatFunction<T> {
FlatFunction {
statements: self.statements.into_iter().collect(),
arguments: self.arguments,
return_count: self.return_count,
}
}
}
impl<T: Field> fmt::Display for FlatFunction<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"def main({}):\n{}",
"def main({}) -> {}:\n{}",
self.arguments
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(","),
self.return_count,
self.statements
.iter()
.map(|x| format!("\t{}", x))
@ -143,21 +144,6 @@ impl<T: Field> fmt::Display for FlatFunction<T> {
}
}
impl<T: Field> fmt::Debug for FlatFunction<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"FlatFunction(arguments: {:?}):\n{}",
self.arguments,
self.statements
.iter()
.map(|x| format!("\t{:?}", x))
.collect::<Vec<_>>()
.join("\n"),
)
}
}
/// Calculates a flattened function based on a R1CS (A, B, C) and returns that flattened function:
/// * The Rank 1 Constraint System (R1CS) is defined as:
/// * `<A,x>*<B,x> = <C,x>` for a witness `x`
@ -168,9 +154,8 @@ impl<T: Field> fmt::Debug for FlatFunction<T> {
///
/// * r1cs - R1CS in standard JSON data format
#[derive(Clone, PartialEq)]
pub enum FlatStatement<T: Field> {
Return(FlatExpressionList<T>),
#[derive(Clone, PartialEq, Debug)]
pub enum FlatStatement<T> {
Condition(FlatExpression<T>, FlatExpression<T>, RuntimeError),
Definition(FlatVariable, FlatExpression<T>),
Directive(FlatDirective<T>),
@ -180,7 +165,6 @@ impl<T: Field> fmt::Display for FlatStatement<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FlatStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
FlatStatement::Return(ref expr) => write!(f, "return {}", expr),
FlatStatement::Condition(ref lhs, ref rhs, ref message) => {
write!(f, "{} == {} // {}", lhs, rhs, message)
}
@ -189,19 +173,6 @@ impl<T: Field> fmt::Display for FlatStatement<T> {
}
}
impl<T: Field> fmt::Debug for FlatStatement<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FlatStatement::Definition(ref lhs, ref rhs) => write!(f, "{} = {}", lhs, rhs),
FlatStatement::Return(ref expr) => write!(f, "FlatReturn({:?})", expr),
FlatStatement::Condition(ref lhs, ref rhs, ref error) => {
write!(f, "FlatCondition({:?}, {:?}, {:?})", lhs, rhs, error)
}
FlatStatement::Directive(ref d) => write!(f, "{:?}", d),
}
}
}
impl<T: Field> FlatStatement<T> {
pub fn apply_substitution(
self,
@ -212,7 +183,6 @@ impl<T: Field> FlatStatement<T> {
*id.apply_substitution(substitution),
x.apply_substitution(substitution),
),
FlatStatement::Return(x) => FlatStatement::Return(x.apply_substitution(substitution)),
FlatStatement::Condition(x, y, message) => FlatStatement::Condition(
x.apply_substitution(substitution),
y.apply_substitution(substitution),
@ -241,13 +211,13 @@ impl<T: Field> FlatStatement<T> {
}
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub struct FlatDirective<T: Field> {
pub struct FlatDirective<T> {
pub inputs: Vec<FlatExpression<T>>,
pub outputs: Vec<FlatVariable>,
pub solver: Solver,
}
impl<T: Field> FlatDirective<T> {
impl<T> FlatDirective<T> {
pub fn new<E: Into<FlatExpression<T>>>(
outputs: Vec<FlatVariable>,
solver: Solver,
@ -284,7 +254,7 @@ impl<T: Field> fmt::Display for FlatDirective<T> {
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum FlatExpression<T> {
Number(T),
Identifier(FlatVariable),
@ -358,62 +328,12 @@ impl<T: Field> fmt::Display for FlatExpression<T> {
}
}
impl<T: fmt::Debug> fmt::Debug for FlatExpression<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FlatExpression::Number(ref i) => write!(f, "Num({:?})", i),
FlatExpression::Identifier(ref var) => write!(f, "Ide({})", var),
FlatExpression::Add(ref lhs, ref rhs) => write!(f, "Add({:?}, {:?})", lhs, rhs),
FlatExpression::Sub(ref lhs, ref rhs) => write!(f, "Sub({:?}, {:?})", lhs, rhs),
FlatExpression::Mult(ref lhs, ref rhs) => write!(f, "Mult({:?}, {:?})", lhs, rhs),
}
}
}
impl<T: Field> From<FlatVariable> for FlatExpression<T> {
fn from(v: FlatVariable) -> FlatExpression<T> {
FlatExpression::Identifier(v)
}
}
#[derive(Clone, PartialEq)]
pub struct FlatExpressionList<T> {
pub expressions: Vec<FlatExpression<T>>,
}
impl<T: Field> fmt::Display for FlatExpressionList<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, param) in self.expressions.iter().enumerate() {
write!(f, "{}", param)?;
if i < self.expressions.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "")
}
}
impl<T: Field> FlatExpressionList<T> {
pub fn apply_substitution(
self,
substitution: &HashMap<FlatVariable, FlatVariable>,
) -> FlatExpressionList<T> {
FlatExpressionList {
expressions: self
.expressions
.into_iter()
.map(|e| e.apply_substitution(substitution))
.collect(),
}
}
}
impl<T: Field> fmt::Debug for FlatExpressionList<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ExpressionList({:?})", self.expressions)
}
}
#[derive(PartialEq, Debug)]
pub struct Error {
message: String,

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@ use crate::ir::*;
use zokrates_field::Field;
pub trait Folder<T: Field>: Sized {
fn fold_module(&mut self, p: Prog<T>) -> Prog<T> {
fold_module(self, p)
fn fold_program(&mut self, p: Prog<T>) -> Prog<T> {
fold_program(self, p)
}
fn fold_argument(&mut self, p: FlatParameter) -> FlatParameter {
@ -34,7 +34,7 @@ pub trait Folder<T: Field>: Sized {
}
}
pub fn fold_module<T: Field, F: Folder<T>>(f: &mut F, p: Prog<T>) -> Prog<T> {
pub fn fold_program<T: Field, F: Folder<T>>(f: &mut F, p: Prog<T>) -> Prog<T> {
Prog {
arguments: p
.arguments
@ -46,7 +46,7 @@ pub fn fold_module<T: Field, F: Folder<T>>(f: &mut F, p: Prog<T>) -> Prog<T> {
.into_iter()
.flat_map(|s| f.fold_statement(s))
.collect(),
returns: p.returns.into_iter().map(|v| f.fold_variable(v)).collect(),
return_count: p.return_count,
}
}

View file

@ -1,5 +1,7 @@
use crate::flat_absy::{FlatDirective, FlatExpression, FlatProg, FlatStatement, FlatVariable};
use crate::ir::{Directive, LinComb, Prog, QuadComb, Statement};
use crate::flat_absy::{
FlatDirective, FlatExpression, FlatProgIterator, FlatStatement, FlatVariable,
};
use crate::ir::{Directive, LinComb, ProgIterator, QuadComb, Statement};
use zokrates_field::Field;
impl<T: Field> QuadComb<T> {
@ -17,49 +19,13 @@ impl<T: Field> QuadComb<T> {
}
}
impl<T: Field> From<FlatProg<T>> for Prog<T> {
fn from(flat_prog: FlatProg<T>) -> Prog<T> {
// get the main function
let main = flat_prog.main;
let return_expressions: Vec<FlatExpression<T>> = main
.statements
.iter()
.filter_map(|s| match s {
FlatStatement::Return(el) => Some(el.expressions.clone()),
_ => None,
})
.next()
.unwrap();
Prog {
arguments: main.arguments,
returns: return_expressions
.iter()
.enumerate()
.map(|(index, _)| FlatVariable::public(index))
.collect(),
statements: main
.statements
.into_iter()
.filter_map(|s| match s {
FlatStatement::Return(..) => None,
s => Some(s.into()),
})
.chain(
return_expressions
.into_iter()
.enumerate()
.map(|(index, expression)| {
Statement::Constraint(
QuadComb::from_flat_expression(expression),
FlatVariable::public(index).into(),
None,
)
}),
)
.collect(),
}
pub fn from_flat<T: Field, I: IntoIterator<Item = FlatStatement<T>>>(
flat_prog_iterator: FlatProgIterator<T, I>,
) -> ProgIterator<T, impl IntoIterator<Item = Statement<T>>> {
ProgIterator {
statements: flat_prog_iterator.statements.into_iter().map(Into::into),
arguments: flat_prog_iterator.arguments,
return_count: flat_prog_iterator.return_count,
}
}
@ -79,6 +45,10 @@ impl<T: Field> From<FlatExpression<T>> for LinComb<T> {
box FlatExpression::Identifier(v1),
box FlatExpression::Number(n1),
) => LinComb::summand(n1, v1),
FlatExpression::Mult(
box FlatExpression::Number(n1),
box FlatExpression::Number(n2),
) => LinComb::summand(n1 * n2, FlatVariable::one()),
e => unreachable!("{}", e),
}
}
@ -104,7 +74,6 @@ impl<T: Field> From<FlatStatement<T>> for Statement<T> {
e => Statement::Constraint(LinComb::from(e).into(), var.into(), None),
},
FlatStatement::Directive(ds) => Statement::Directive(ds.into()),
_ => panic!("return should be handled at the function level"),
}
}
}

View file

@ -1,6 +1,6 @@
use crate::flat_absy::flat_variable::FlatVariable;
use crate::flat_absy::RuntimeError;
use crate::ir::{LinComb, Prog, QuadComb, Statement, Witness};
use crate::ir::{LinComb, ProgIterator, QuadComb, Statement, Witness};
use crate::solvers::Solver;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@ -9,8 +9,6 @@ use zokrates_field::Field;
pub type ExecutionResult<T> = Result<Witness<T>, Error>;
impl<T: Field> Prog<T> {}
#[derive(Default)]
pub struct Interpreter {
/// Whether we should try to give out-of-range bit decompositions when the input is not a single summand.
@ -27,8 +25,12 @@ impl Interpreter {
}
impl Interpreter {
pub fn execute<T: Field>(&self, program: &Prog<T>, inputs: &[T]) -> ExecutionResult<T> {
self.check_inputs(program, inputs)?;
pub fn execute<T: Field, I: IntoIterator<Item = Statement<T>>>(
&self,
program: ProgIterator<T, I>,
inputs: &[T],
) -> ExecutionResult<T> {
self.check_inputs(&program, inputs)?;
let mut witness = BTreeMap::new();
witness.insert(FlatVariable::one(), T::one());
@ -36,7 +38,7 @@ impl Interpreter {
witness.insert(arg.id, value.clone());
}
for statement in program.statements.iter() {
for statement in program.statements.into_iter() {
match statement {
Statement::Constraint(quad, lin, error) => match lin.is_assignee(&witness) {
true => {
@ -47,9 +49,7 @@ impl Interpreter {
let lhs_value = quad.evaluate(&witness).unwrap();
let rhs_value = lin.evaluate(&witness).unwrap();
if lhs_value != rhs_value {
return Err(Error::UnsatisfiedConstraint {
error: error.to_owned(),
});
return Err(Error::UnsatisfiedConstraint { error });
}
}
},
@ -108,7 +108,11 @@ impl Interpreter {
.collect()
}
fn check_inputs<T: Field, U>(&self, program: &Prog<T>, inputs: &[U]) -> Result<(), Error> {
fn check_inputs<T: Field, I: IntoIterator<Item = Statement<T>>, U>(
&self,
program: &ProgIterator<T, I>,
inputs: &[U],
) -> Result<(), Error> {
if program.arguments.len() == inputs.len() {
Ok(())
} else {

View file

@ -8,7 +8,7 @@ use zokrates_field::Field;
mod expression;
pub mod folder;
mod from_flat;
pub mod from_flat;
mod interpreter;
mod serialize;
pub mod smtlib2;
@ -74,25 +74,30 @@ impl<T: Field> fmt::Display for Statement<T> {
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq, Default)]
pub struct Prog<T> {
pub statements: Vec<Statement<T>>,
pub type Prog<T> = ProgIterator<T, Vec<Statement<T>>>;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct ProgIterator<T, I: IntoIterator<Item = Statement<T>>> {
pub arguments: Vec<FlatParameter>,
pub returns: Vec<FlatVariable>,
pub return_count: usize,
pub statements: I,
}
impl<T: Field> Prog<T> {
pub fn constraint_count(&self) -> usize {
self.statements
.iter()
.filter(|s| matches!(s, Statement::Constraint(..)))
.count()
impl<T, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn collect(self) -> ProgIterator<T, Vec<Statement<T>>> {
ProgIterator {
statements: self.statements.into_iter().collect::<Vec<_>>(),
arguments: self.arguments,
return_count: self.return_count,
}
}
pub fn arguments_count(&self) -> usize {
self.arguments.len()
pub fn returns(&self) -> Vec<FlatVariable> {
(0..self.return_count).map(FlatVariable::public).collect()
}
}
impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn public_inputs(&self, witness: &Witness<T>) -> Vec<T> {
self.arguments
.iter()
@ -103,24 +108,43 @@ impl<T: Field> Prog<T> {
}
}
impl<T> Prog<T> {
pub fn constraint_count(&self) -> usize {
self.statements
.iter()
.filter(|s| matches!(s, Statement::Constraint(..)))
.count()
}
pub fn into_prog_iter(self) -> ProgIterator<T, impl IntoIterator<Item = Statement<T>>> {
ProgIterator {
statements: self.statements.into_iter(),
arguments: self.arguments,
return_count: self.return_count,
}
}
}
impl<T: Field> fmt::Display for Prog<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
writeln!(
f,
"def main({}) -> ({}):\n{}\n\treturn {}",
"def main({}) -> ({}):",
self.arguments
.iter()
.map(|v| format!("{}", v))
.collect::<Vec<_>>()
.join(", "),
self.returns.len(),
self.statements
.iter()
.map(|s| format!("\t{}", s))
.collect::<Vec<_>>()
.join("\n"),
self.returns
.iter()
self.return_count,
)?;
for s in &self.statements {
writeln!(f, "\t{}", s)?;
}
writeln!(
f,
"\treturn {}",
(0..self.return_count)
.map(FlatVariable::public)
.map(|e| format!("{}", e))
.collect::<Vec<_>>()
.join(", ")

View file

@ -1,5 +1,5 @@
use crate::ir::Prog;
use bincode::{deserialize_from, serialize_into, Infinite};
use crate::ir::{Prog, ProgIterator, Statement};
use serde_cbor;
use std::io::{Read, Write};
use zokrates_field::*;
@ -7,24 +7,115 @@ const ZOKRATES_MAGIC: &[u8; 4] = &[0x5a, 0x4f, 0x4b, 0];
const ZOKRATES_VERSION_1: &[u8; 4] = &[0, 0, 0, 1];
#[derive(PartialEq, Debug)]
pub enum ProgEnum {
Bls12_381Program(Prog<Bls12_381Field>),
Bn128Program(Prog<Bn128Field>),
Bls12_377Program(Prog<Bls12_377Field>),
Bw6_761Program(Prog<Bw6_761Field>),
pub enum ProgEnum<
Bls12_381I: IntoIterator<Item = Statement<Bls12_381Field>>,
Bn128I: IntoIterator<Item = Statement<Bn128Field>>,
Bls12_377I: IntoIterator<Item = Statement<Bls12_377Field>>,
Bw6_761I: IntoIterator<Item = Statement<Bw6_761Field>>,
> {
Bls12_381Program(ProgIterator<Bls12_381Field, Bls12_381I>),
Bn128Program(ProgIterator<Bn128Field, Bn128I>),
Bls12_377Program(ProgIterator<Bls12_377Field, Bls12_377I>),
Bw6_761Program(ProgIterator<Bw6_761Field, Bw6_761I>),
}
impl<T: Field> Prog<T> {
pub fn serialize<W: Write>(&self, mut w: W) {
type MemoryProgEnum = ProgEnum<
Vec<Statement<Bls12_381Field>>,
Vec<Statement<Bn128Field>>,
Vec<Statement<Bls12_377Field>>,
Vec<Statement<Bw6_761Field>>,
>;
use serde::{Serialize, Serializer};
use std::cell::Cell;
impl<
Bls12_381I: IntoIterator<Item = Statement<Bls12_381Field>>,
Bn128I: IntoIterator<Item = Statement<Bn128Field>>,
Bls12_377I: IntoIterator<Item = Statement<Bls12_377Field>>,
Bw6_761I: IntoIterator<Item = Statement<Bw6_761Field>>,
> ProgEnum<Bls12_381I, Bn128I, Bls12_377I, Bw6_761I>
{
pub fn collect(self) -> MemoryProgEnum {
match self {
ProgEnum::Bls12_381Program(p) => ProgEnum::Bls12_381Program(p.collect()),
ProgEnum::Bn128Program(p) => ProgEnum::Bn128Program(p.collect()),
ProgEnum::Bls12_377Program(p) => ProgEnum::Bls12_377Program(p.collect()),
ProgEnum::Bw6_761Program(p) => ProgEnum::Bw6_761Program(p.collect()),
}
}
}
pub fn write_as_cbor<T: Serialize + std::fmt::Debug, I, W>(
out: &mut W,
prog_iterator: ProgIterator<T, I>,
) -> serde_cbor::Result<()>
where
I: IntoIterator<Item = Statement<T>>,
W: Write,
{
struct Wrapper<U>(Cell<Option<U>>);
struct SerializableProgIterator<T: Serialize, I: IntoIterator<Item = Statement<T>>> {
arguments: Vec<crate::flat_absy::FlatParameter>,
return_count: usize,
statements: Wrapper<I>,
}
impl<T, I> Serialize for SerializableProgIterator<T, I>
where
T: Serialize + std::fmt::Debug,
I: IntoIterator<Item = Statement<T>>,
{
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut spi = s.serialize_struct("SerializableProgIterator", 3)?;
spi.serialize_field("arguments", &self.arguments)?;
spi.serialize_field("return_count", &self.return_count)?;
spi.serialize_field("statements", &self.statements)?;
spi.end()
}
}
impl<I, P> Serialize for Wrapper<I>
where
I: IntoIterator<Item = P>,
P: Serialize + std::fmt::Debug,
{
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.collect_seq(self.0.take().unwrap())
}
}
serde_cbor::to_writer(
out,
&SerializableProgIterator {
arguments: prog_iterator.arguments,
return_count: prog_iterator.return_count,
statements: Wrapper(Cell::new(Some(prog_iterator.statements))),
},
)
}
impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn serialize<W: Write>(self, mut w: W) {
w.write_all(ZOKRATES_MAGIC).unwrap();
w.write_all(ZOKRATES_VERSION_1).unwrap();
w.write_all(&T::id()).unwrap();
serialize_into(&mut w, self, Infinite).unwrap();
write_as_cbor(&mut w, self).unwrap();
}
}
impl ProgEnum {
impl
ProgEnum<
std::vec::IntoIter<Statement<Bls12_381Field>>,
std::vec::IntoIter<Statement<Bn128Field>>,
std::vec::IntoIter<Statement<Bls12_377Field>>,
std::vec::IntoIter<Statement<Bw6_761Field>>,
>
{
pub fn deserialize<R: Read>(mut r: R) -> Result<Self, String> {
// Check the magic number, `ZOK`
let mut magic = [0; 4];
@ -44,18 +135,42 @@ impl ProgEnum {
.map_err(|_| String::from("Cannot read curve identifier"))?;
match curve {
m if m == Bls12_381Field::id() => Ok(ProgEnum::Bls12_381Program(
deserialize_from(&mut r, Infinite).unwrap(),
)),
m if m == Bn128Field::id() => Ok(ProgEnum::Bn128Program(
deserialize_from(&mut r, Infinite).unwrap(),
)),
m if m == Bls12_377Field::id() => Ok(ProgEnum::Bls12_377Program(
deserialize_from(&mut r, Infinite).unwrap(),
)),
m if m == Bw6_761Field::id() => Ok(ProgEnum::Bw6_761Program(
deserialize_from(&mut r, Infinite).unwrap(),
)),
m if m == Bls12_381Field::id() => {
let p: Prog<Bls12_381Field> = serde_cbor::from_reader(r).unwrap();
Ok(ProgEnum::Bls12_381Program(ProgIterator {
statements: p.statements.into_iter(),
arguments: p.arguments,
return_count: p.return_count,
}))
}
m if m == Bn128Field::id() => {
let p: Prog<Bn128Field> = serde_cbor::from_reader(r).unwrap();
Ok(ProgEnum::Bn128Program(ProgIterator {
statements: p.statements.into_iter(),
arguments: p.arguments,
return_count: p.return_count,
}))
}
m if m == Bls12_377Field::id() => {
let p: Prog<Bls12_377Field> = serde_cbor::from_reader(r).unwrap();
Ok(ProgEnum::Bls12_377Program(ProgIterator {
statements: p.statements.into_iter(),
arguments: p.arguments,
return_count: p.return_count,
}))
}
m if m == Bw6_761Field::id() => {
let p: Prog<Bw6_761Field> = serde_cbor::from_reader(r).unwrap();
Ok(ProgEnum::Bw6_761Program(ProgIterator {
statements: p.statements.into_iter(),
arguments: p.arguments,
return_count: p.return_count,
}))
}
_ => Err(String::from("Unknown curve identifier")),
}
} else {
@ -79,7 +194,7 @@ mod tests {
let p: ir::Prog<Bn128Field> = ir::Prog::default();
let mut buffer = Cursor::new(vec![]);
p.serialize(&mut buffer);
p.clone().serialize(&mut buffer);
// rewind back to the beginning of the file
buffer.seek(SeekFrom::Start(0)).unwrap();
@ -87,12 +202,12 @@ mod tests {
// deserialize
let deserialized_p = ProgEnum::deserialize(buffer).unwrap();
assert_eq!(ProgEnum::Bn128Program(p), deserialized_p);
assert_eq!(ProgEnum::Bn128Program(p), deserialized_p.collect());
let p: ir::Prog<Bls12_381Field> = ir::Prog::default();
let mut buffer = Cursor::new(vec![]);
p.serialize(&mut buffer);
p.clone().serialize(&mut buffer);
// rewind back to the beginning of the file
buffer.seek(SeekFrom::Start(0)).unwrap();
@ -100,6 +215,6 @@ mod tests {
// deserialize
let deserialized_p = ProgEnum::deserialize(buffer).unwrap();
assert_eq!(ProgEnum::Bls12_381Program(p), deserialized_p);
assert_eq!(ProgEnum::Bls12_381Program(p), deserialized_p.collect());
}
}

View file

@ -49,9 +49,6 @@ pub fn visit_module<T: Field, F: Visitor<T>>(f: &mut F, p: &Prog<T>) {
for expr in p.statements.iter() {
f.visit_statement(expr);
}
for expr in p.returns.iter() {
f.visit_variable(expr);
}
}
pub fn visit_statement<T: Field, F: Visitor<T>>(f: &mut F, s: &Statement<T>) {

View file

@ -1,6 +1,7 @@
use crate::ir::{folder::Folder, LinComb};
use zokrates_field::Field;
#[derive(Default)]
pub struct Canonicalizer;
impl<T: Field> Folder<T> for Canonicalizer {

View file

@ -12,49 +12,18 @@
use crate::flat_absy::flat_variable::FlatVariable;
use crate::ir::folder::*;
use crate::ir::*;
use crate::optimizer::canonicalizer::Canonicalizer;
use crate::solvers::Solver;
use std::collections::hash_map::{Entry, HashMap};
use zokrates_field::Field;
#[derive(Debug)]
pub struct DirectiveOptimizer<T: Field> {
#[derive(Debug, Default)]
pub struct DirectiveOptimizer<T> {
calls: HashMap<(Solver, Vec<QuadComb<T>>), Vec<FlatVariable>>,
/// Map of renamings for reassigned variables while processing the program.
substitution: HashMap<FlatVariable, FlatVariable>,
}
impl<T: Field> DirectiveOptimizer<T> {
fn new() -> DirectiveOptimizer<T> {
DirectiveOptimizer {
calls: HashMap::new(),
substitution: HashMap::new(),
}
}
pub fn optimize(p: Prog<T>) -> Prog<T> {
DirectiveOptimizer::new().fold_module(p)
}
}
impl<T: Field> Folder<T> for DirectiveOptimizer<T> {
fn fold_module(&mut self, p: Prog<T>) -> Prog<T> {
// in order to correctly identify duplicates, we need to first canonicalize the statements
let mut canonicalizer = Canonicalizer;
let p = Prog {
statements: p
.statements
.into_iter()
.flat_map(|s| canonicalizer.fold_statement(s))
.collect(),
..p
};
fold_module(self, p)
}
fn fold_variable(&mut self, v: FlatVariable) -> FlatVariable {
*self.substitution.get(&v).unwrap_or(&v)
}

View file

@ -16,25 +16,13 @@ fn hash<T: Field>(s: &Statement<T>) -> Hash {
hasher.finish()
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct DuplicateOptimizer {
seen: HashSet<Hash>,
}
impl DuplicateOptimizer {
fn new() -> Self {
DuplicateOptimizer {
seen: HashSet::new(),
}
}
pub fn optimize<T: Field>(p: Prog<T>) -> Prog<T> {
Self::new().fold_module(p)
}
}
impl<T: Field> Folder<T> for DuplicateOptimizer {
fn fold_module(&mut self, p: Prog<T>) -> Prog<T> {
fn fold_program(&mut self, p: Prog<T>) -> Prog<T> {
// in order to correctly identify duplicates, we need to first canonicalize the statements
let mut canonicalizer = Canonicalizer;
@ -47,7 +35,7 @@ impl<T: Field> Folder<T> for DuplicateOptimizer {
..p
};
fold_module(self, p)
fold_program(self, p)
}
fn fold_statement(&mut self, s: Statement<T>) -> Vec<Statement<T>> {
@ -87,13 +75,16 @@ mod tests {
LinComb::zero(),
),
],
returns: vec![],
return_count: 0,
arguments: vec![],
};
let expected = p.clone();
assert_eq!(DuplicateOptimizer::optimize(p), expected);
assert_eq!(
DuplicateOptimizer::default().fold_program(p).collect(),
expected
);
}
#[test]
@ -120,7 +111,7 @@ mod tests {
constraint.clone(),
constraint.clone(),
],
returns: vec![],
return_count: 0,
arguments: vec![],
};
@ -135,10 +126,13 @@ mod tests {
LinComb::zero(),
),
],
returns: vec![],
return_count: 0,
arguments: vec![],
};
assert_eq!(DuplicateOptimizer::optimize(p), expected);
assert_eq!(
DuplicateOptimizer::default().fold_program(p).collect(),
expected
);
}
}

View file

@ -10,41 +10,56 @@ mod duplicate;
mod redefinition;
mod tautology;
use self::canonicalizer::Canonicalizer;
use self::directive::DirectiveOptimizer;
use self::duplicate::DuplicateOptimizer;
use self::redefinition::RedefinitionOptimizer;
use self::tautology::TautologyOptimizer;
use crate::ir::Prog;
use crate::ir::{ProgIterator, Statement};
use zokrates_field::Field;
impl<T: Field> Prog<T> {
pub fn optimize(self) -> Self {
impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn optimize(self) -> ProgIterator<T, impl IntoIterator<Item = Statement<T>>> {
// remove redefinitions
log::debug!("Constraints: {}", self.constraint_count());
log::debug!("Optimizer: Remove redefinitions");
let r = RedefinitionOptimizer::optimize(self);
log::trace!("\n{}\n", r);
log::debug!(
"Optimizer: Remove redefinitions and tautologies and directives and duplicates"
);
// remove constraints that are always satisfied
log::debug!("Constraints: {}", r.constraint_count());
log::debug!("Optimizer: Remove tautologies");
let r = TautologyOptimizer::optimize(r);
log::trace!("\n{}\n", r);
// define all optimizer steps
let mut redefinition_optimizer = RedefinitionOptimizer::init(&self);
let mut tautologies_optimizer = TautologyOptimizer::default();
let mut directive_optimizer = DirectiveOptimizer::default();
let mut canonicalizer = Canonicalizer::default();
let mut duplicate_optimizer = DuplicateOptimizer::default();
// deduplicate directives which take the same input
log::debug!("Constraints: {}", r.constraint_count());
log::debug!("Optimizer: Remove duplicate directive");
let r = DirectiveOptimizer::optimize(r);
log::trace!("\n{}\n", r);
use crate::ir::folder::Folder;
// remove duplicate constraints
log::debug!("Constraints: {}", r.constraint_count());
log::debug!("Optimizer: Remove duplicate constraints");
let r = DuplicateOptimizer::optimize(r);
log::trace!("\n{}\n", r);
let r = ProgIterator {
arguments: self
.arguments
.into_iter()
.map(|a| redefinition_optimizer.fold_argument(a))
.map(|a| {
<TautologyOptimizer as Folder<T>>::fold_argument(&mut tautologies_optimizer, a)
})
.map(|a| directive_optimizer.fold_argument(a))
.map(|a| {
<DuplicateOptimizer as Folder<T>>::fold_argument(&mut duplicate_optimizer, a)
})
.collect(),
statements: self
.statements
.into_iter()
.flat_map(move |s| redefinition_optimizer.fold_statement(s))
.flat_map(move |s| tautologies_optimizer.fold_statement(s))
.flat_map(move |s| canonicalizer.fold_statement(s))
.flat_map(move |s| directive_optimizer.fold_statement(s))
.flat_map(move |s| duplicate_optimizer.fold_statement(s)),
return_count: self.return_count,
};
log::debug!("Constraints: {}", r.constraint_count());
log::debug!("Done");
r
}
}

View file

@ -37,51 +37,35 @@
// - otherwise return `c_0`
use crate::flat_absy::flat_variable::FlatVariable;
use crate::flat_absy::FlatParameter;
use crate::ir::folder::{fold_module, Folder};
use crate::ir::folder::Folder;
use crate::ir::LinComb;
use crate::ir::*;
use std::collections::{HashMap, HashSet};
use zokrates_field::Field;
#[derive(Debug)]
pub struct RedefinitionOptimizer<T: Field> {
pub struct RedefinitionOptimizer<T> {
/// Map of renamings for reassigned variables while processing the program.
substitution: HashMap<FlatVariable, CanonicalLinComb<T>>,
/// Set of variables that should not be substituted
ignore: HashSet<FlatVariable>,
pub ignore: HashSet<FlatVariable>,
}
impl<T: Field> RedefinitionOptimizer<T> {
fn new() -> Self {
impl<T> RedefinitionOptimizer<T> {
pub fn init<I: IntoIterator<Item = Statement<T>>>(p: &ProgIterator<T, I>) -> Self {
RedefinitionOptimizer {
substitution: HashMap::new(),
ignore: HashSet::new(),
ignore: vec![FlatVariable::one()]
.into_iter()
.chain(p.arguments.iter().map(|p| p.id))
.chain(p.returns())
.into_iter()
.collect(),
}
}
pub fn optimize(p: Prog<T>) -> Prog<T> {
RedefinitionOptimizer::new().fold_module(p)
}
}
impl<T: Field> Folder<T> for RedefinitionOptimizer<T> {
fn fold_module(&mut self, p: Prog<T>) -> Prog<T> {
// to prevent the optimiser from replacing outputs, add them to the ignored set
self.ignore.extend(p.returns.iter().cloned());
// to prevent the optimiser from replacing ~one, add it to the ignored set
self.ignore.insert(FlatVariable::one());
fold_module(self, p)
}
fn fold_argument(&mut self, a: FlatParameter) -> FlatParameter {
// to prevent the optimiser from replacing user input, add it to the ignored set
self.ignore.insert(a.id);
a
}
fn fold_statement(&mut self, s: Statement<T>) -> Vec<Statement<T>> {
match s {
Statement::Constraint(quad, lin, message) => {
@ -225,29 +209,31 @@ mod tests {
let x = FlatParameter::public(FlatVariable::new(0));
let y = FlatVariable::new(1);
let z = FlatVariable::new(2);
let out = FlatVariable::public(0);
let p: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![Statement::definition(y, x.id), Statement::definition(z, y)],
returns: vec![z],
statements: vec![
Statement::definition(y, x.id),
Statement::definition(out, y),
],
return_count: 1,
};
let optimized: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![Statement::definition(z, x.id)],
returns: vec![z],
statements: vec![Statement::definition(out, x.id)],
return_count: 1,
};
let mut optimizer = RedefinitionOptimizer::new();
assert_eq!(optimizer.fold_module(p), optimized);
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_program(p), optimized);
}
#[test]
fn keep_one() {
// def main(x):
// one = x
// return one
let one = FlatVariable::one();
let x = FlatParameter::public(FlatVariable::new(0));
@ -255,13 +241,13 @@ mod tests {
let p: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![Statement::definition(one, x.id)],
returns: vec![x.id],
return_count: 1,
};
let optimized = p.clone();
let mut optimizer = RedefinitionOptimizer::new();
assert_eq!(optimizer.fold_module(p), optimized);
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_program(p), optimized);
}
#[test]
@ -270,7 +256,7 @@ mod tests {
// y = x
// z = y
// z == y
// return z
// ~out_0 = z
// ->
@ -281,6 +267,7 @@ mod tests {
let x = FlatParameter::public(FlatVariable::new(0));
let y = FlatVariable::new(1);
let z = FlatVariable::new(2);
let out = FlatVariable::public(0);
let p: Prog<Bn128Field> = Prog {
arguments: vec![x],
@ -288,21 +275,22 @@ mod tests {
Statement::definition(y, x.id),
Statement::definition(z, y),
Statement::constraint(z, y),
Statement::definition(out, z),
],
returns: vec![z],
return_count: 1,
};
let optimized: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![
Statement::definition(z, x.id),
Statement::constraint(z, x.id),
Statement::constraint(x.id, x.id),
Statement::definition(out, x.id),
],
returns: vec![z],
return_count: 1,
};
let mut optimizer = RedefinitionOptimizer::new();
assert_eq!(optimizer.fold_module(p), optimized);
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_program(p), optimized);
}
#[test]
@ -312,7 +300,8 @@ mod tests {
// t = 1
// z = y
// w = t
// return z, w
// ~out_0 = z
// ~out_1 = w
// ->
@ -324,6 +313,8 @@ mod tests {
let z = FlatVariable::new(2);
let t = FlatVariable::new(3);
let w = FlatVariable::new(4);
let out_1 = FlatVariable::public(0);
let out_0 = FlatVariable::public(1);
let p: Prog<Bn128Field> = Prog {
arguments: vec![x],
@ -332,22 +323,24 @@ mod tests {
Statement::definition(t, Bn128Field::from(1)),
Statement::definition(z, y),
Statement::definition(w, t),
Statement::definition(out_0, z),
Statement::definition(out_1, w),
],
returns: vec![z, w],
return_count: 2,
};
let optimized: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![
Statement::definition(z, x.id),
Statement::definition(w, Bn128Field::from(1)),
Statement::definition(out_0, x.id),
Statement::definition(out_1, Bn128Field::from(1)),
],
returns: vec![z, w],
return_count: 2,
};
let mut optimizer = RedefinitionOptimizer::new();
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_module(p), optimized);
assert_eq!(optimizer.fold_program(p), optimized);
}
#[test]
@ -357,21 +350,20 @@ mod tests {
// b = a + x + y
// c = b + x + y
// 2*c == 6*x + 6*y
// r = a + b + c
// return r
// ~out_0 = a + b + c
// ->
// def main(x, y) -> (1):
// 1*x + 1*y + 2*x + 2*y + 3*x + 3*y == 6*x + 6*y // will be eliminated as a tautology
// return 6*x + 6*y
// ~out_0 = 6*x + 6*y
let x = FlatParameter::public(FlatVariable::new(0));
let y = FlatParameter::public(FlatVariable::new(1));
let a = FlatVariable::new(2);
let b = FlatVariable::new(3);
let c = FlatVariable::new(4);
let r = FlatVariable::new(5);
let r = FlatVariable::public(0);
let p: Prog<Bn128Field> = Prog {
arguments: vec![x, y],
@ -391,7 +383,7 @@ mod tests {
),
Statement::definition(r, LinComb::from(a) + LinComb::from(b) + LinComb::from(c)),
],
returns: vec![r],
return_count: 1,
};
let expected: Prog<Bn128Field> = Prog {
@ -411,12 +403,12 @@ mod tests {
+ LinComb::summand(3, y.id),
),
],
returns: vec![r],
return_count: 1,
};
let mut optimizer = RedefinitionOptimizer::new();
let mut optimizer = RedefinitionOptimizer::init(&p);
let optimized = optimizer.fold_module(p);
let optimized = optimizer.fold_program(p);
assert_eq!(optimized, expected);
}
@ -448,13 +440,13 @@ mod tests {
),
Statement::definition(z, LinComb::from(x.id)),
],
returns: vec![],
return_count: 0,
};
let optimized = p.clone();
let mut optimizer = RedefinitionOptimizer::new();
assert_eq!(optimizer.fold_module(p), optimized);
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_program(p), optimized);
}
#[test]
@ -476,12 +468,12 @@ mod tests {
Statement::constraint(x.id, Bn128Field::from(1)),
Statement::constraint(x.id, Bn128Field::from(2)),
],
returns: vec![x.id],
return_count: 1,
};
let optimized = p.clone();
let mut optimizer = RedefinitionOptimizer::new();
assert_eq!(optimizer.fold_module(p), optimized);
let mut optimizer = RedefinitionOptimizer::init(&p);
assert_eq!(optimizer.fold_program(p), optimized);
}
}

View file

@ -10,17 +10,8 @@ use crate::ir::folder::Folder;
use crate::ir::*;
use zokrates_field::Field;
pub struct TautologyOptimizer {}
impl TautologyOptimizer {
fn new() -> TautologyOptimizer {
TautologyOptimizer {}
}
pub fn optimize<T: Field>(p: Prog<T>) -> Prog<T> {
TautologyOptimizer::new().fold_module(p)
}
}
#[derive(Default)]
pub struct TautologyOptimizer;
impl<T: Field> Folder<T> for TautologyOptimizer {
fn fold_statement(&mut self, s: Statement<T>) -> Vec<Statement<T>> {

View file

@ -6,7 +6,7 @@ use ark_gm17::{
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use zokrates_field::{ArkFieldExtensions, Bw6_761Field, Field};
use crate::ir::{Prog, Witness};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::ark::Ark;
use crate::proof_system::ark::Computation;
use crate::proof_system::ark::{parse_fr, parse_g1, parse_g2, parse_g2_fq};
@ -15,7 +15,9 @@ use crate::proof_system::Scheme;
use crate::proof_system::{Backend, NonUniversalBackend, Proof, SetupKeypair};
impl<T: Field + ArkFieldExtensions + NotBw6_761Field> NonUniversalBackend<T, GM17> for Ark {
fn setup(program: Prog<T>) -> SetupKeypair<<GM17 as Scheme<T>>::VerificationKey> {
fn setup<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
) -> SetupKeypair<<GM17 as Scheme<T>>::VerificationKey> {
let parameters = Computation::without_witness(program).setup();
let mut pk: Vec<u8> = Vec::new();
@ -40,23 +42,12 @@ impl<T: Field + ArkFieldExtensions + NotBw6_761Field> NonUniversalBackend<T, GM1
}
impl<T: Field + ArkFieldExtensions + NotBw6_761Field> Backend<T, GM17> for Ark {
fn generate_proof(
program: Prog<T>,
fn generate_proof<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
) -> Proof<<GM17 as Scheme<T>>::ProofPoints> {
let computation = Computation::with_witness(program, witness);
let params = ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_uncompressed(
&mut proving_key.as_slice(),
)
.unwrap();
let proof = computation.clone().prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
c: parse_g1::<T>(&proof.c),
};
let inputs = computation
.public_inputs_values()
@ -64,6 +55,18 @@ impl<T: Field + ArkFieldExtensions + NotBw6_761Field> Backend<T, GM17> for Ark {
.map(parse_fr::<T>)
.collect::<Vec<_>>();
let params = ProvingKey::<<T as ArkFieldExtensions>::ArkEngine>::deserialize_uncompressed(
&mut proving_key.as_slice(),
)
.unwrap();
let proof = computation.prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
c: parse_g1::<T>(&proof.c),
};
Proof::new(proof_points, inputs)
}
@ -108,8 +111,8 @@ impl<T: Field + ArkFieldExtensions + NotBw6_761Field> Backend<T, GM17> for Ark {
}
impl NonUniversalBackend<Bw6_761Field, GM17> for Ark {
fn setup(
program: Prog<Bw6_761Field>,
fn setup<I: IntoIterator<Item = Statement<Bw6_761Field>>>(
program: ProgIterator<Bw6_761Field, I>,
) -> SetupKeypair<<GM17 as Scheme<Bw6_761Field>>::VerificationKey> {
let parameters = Computation::without_witness(program).setup();
@ -135,24 +138,12 @@ impl NonUniversalBackend<Bw6_761Field, GM17> for Ark {
}
impl Backend<Bw6_761Field, GM17> for Ark {
fn generate_proof(
program: Prog<Bw6_761Field>,
fn generate_proof<I: IntoIterator<Item = Statement<Bw6_761Field>>>(
program: ProgIterator<Bw6_761Field, I>,
witness: Witness<Bw6_761Field>,
proving_key: Vec<u8>,
) -> Proof<<GM17 as Scheme<Bw6_761Field>>::ProofPoints> {
let computation = Computation::with_witness(program, witness);
let params =
ProvingKey::<<Bw6_761Field as ArkFieldExtensions>::ArkEngine>::deserialize_uncompressed(
&mut proving_key.as_slice(),
)
.unwrap();
let proof = computation.clone().prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<Bw6_761Field>(&proof.a),
b: parse_g2_fq::<Bw6_761Field>(&proof.b),
c: parse_g1::<Bw6_761Field>(&proof.c),
};
let inputs = computation
.public_inputs_values()
@ -160,6 +151,19 @@ impl Backend<Bw6_761Field, GM17> for Ark {
.map(parse_fr::<Bw6_761Field>)
.collect::<Vec<_>>();
let params =
ProvingKey::<<Bw6_761Field as ArkFieldExtensions>::ArkEngine>::deserialize_uncompressed(
&mut proving_key.as_slice(),
)
.unwrap();
let proof = computation.prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<Bw6_761Field>(&proof.a),
b: parse_g2_fq::<Bw6_761Field>(&proof.b),
c: parse_g1::<Bw6_761Field>(&proof.c),
};
Proof::new(proof_points, inputs)
}
@ -260,7 +264,7 @@ mod tests {
fn verify_bls12_377_field() {
let program: Prog<Bls12_377Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -271,11 +275,14 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bls12_377Field::from(42)])
.execute(program.clone(), &[Bls12_377Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bls12_377Field, GM17>>::generate_proof(program, witness, keypair.pk);
let proof = <Ark as Backend<Bls12_377Field, GM17>>::generate_proof(
program.into(),
witness,
keypair.pk,
);
let ans = <Ark as Backend<Bls12_377Field, GM17>>::verify(keypair.vk, proof);
assert!(ans);
@ -285,7 +292,7 @@ mod tests {
fn verify_bw6_761_field() {
let program: Prog<Bw6_761Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -296,7 +303,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bw6_761Field::from(42)])
.execute(program.clone(), &[Bw6_761Field::from(42)])
.unwrap();
let proof =

View file

@ -10,7 +10,7 @@ use sha2::Sha256;
use zokrates_field::{ArkFieldExtensions, Field};
use crate::ir::{Prog, Witness};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::ark::parse_fr;
use crate::proof_system::ark::Ark;
use crate::proof_system::ark::Computation;
@ -45,10 +45,12 @@ impl<T: Field + ArkFieldExtensions> UniversalBackend<T, marlin::Marlin> for Ark
res
}
fn setup(
universal_srs: Vec<u8>,
program: Prog<T>,
fn setup<I: IntoIterator<Item = Statement<T>>>(
srs: Vec<u8>,
program: ProgIterator<T, I>,
) -> Result<SetupKeypair<<marlin::Marlin as Scheme<T>>::VerificationKey>, String> {
let program = program.collect();
if program.constraint_count() < MINIMUM_CONSTRAINT_COUNT {
return Err(format!("Programs must have a least {} constraints. This program is too small to generate a setup with Marlin, see [this issue](https://github.com/arkworks-rs/marlin/issues/79)", MINIMUM_CONSTRAINT_COUNT));
}
@ -61,7 +63,7 @@ impl<T: Field + ArkFieldExtensions> UniversalBackend<T, marlin::Marlin> for Ark
T::ArkEngine,
DensePolynomial<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
>,
>::deserialize(&mut universal_srs.as_slice())
>::deserialize(&mut srs.as_slice())
.unwrap();
let (pk, vk) = ArkMarlin::<
@ -91,8 +93,8 @@ impl<T: Field + ArkFieldExtensions> UniversalBackend<T, marlin::Marlin> for Ark
}
impl<T: Field + ArkFieldExtensions> Backend<T, marlin::Marlin> for Ark {
fn generate_proof(
program: Prog<T>,
fn generate_proof<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
) -> Proof<<marlin::Marlin as Scheme<T>>::ProofPoints> {
@ -199,7 +201,7 @@ mod tests {
fn verify_bls12_377_field() {
let program: Prog<Bls12_377Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![
Statement::constraint(
QuadComb::from_linear_combinations(
@ -214,15 +216,19 @@ mod tests {
let srs = <Ark as UniversalBackend<Bls12_377Field, Marlin>>::universal_setup(5);
let keypair =
<Ark as UniversalBackend<Bls12_377Field, Marlin>>::setup(srs, program.clone()).unwrap();
<Ark as UniversalBackend<Bls12_377Field, Marlin>>::setup(srs, program.clone().into())
.unwrap();
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bls12_377Field::from(42)])
.execute(program.clone(), &[Bls12_377Field::from(42)])
.unwrap();
let proof =
<Ark as Backend<Bls12_377Field, Marlin>>::generate_proof(program, witness, keypair.pk);
let proof = <Ark as Backend<Bls12_377Field, Marlin>>::generate_proof(
program.clone(),
witness,
keypair.pk,
);
let ans = <Ark as Backend<Bls12_377Field, Marlin>>::verify(keypair.vk, proof);
assert!(ans);
@ -232,7 +238,7 @@ mod tests {
fn verify_bw6_761_field() {
let program: Prog<Bw6_761Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![
Statement::constraint(
QuadComb::from_linear_combinations(
@ -251,7 +257,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bw6_761Field::from(42)])
.execute(program.clone(), &[Bw6_761Field::from(42)])
.unwrap();
let proof =

View file

@ -1,7 +1,7 @@
pub mod gm17;
pub mod marlin;
use crate::ir::{CanonicalLinComb, Prog, Statement, Witness};
use crate::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use ark_gm17::Proof;
use ark_gm17::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
@ -24,20 +24,20 @@ use rand_0_7::SeedableRng;
pub struct Ark;
#[derive(Clone)]
pub struct Computation<T> {
program: Prog<T>,
pub struct Computation<T, I: IntoIterator<Item = Statement<T>>> {
program: ProgIterator<T, I>,
witness: Option<Witness<T>>,
}
impl<T: Field> Computation<T> {
pub fn with_witness(program: Prog<T>, witness: Witness<T>) -> Self {
impl<T, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn with_witness(program: ProgIterator<T, I>, witness: Witness<T>) -> Self {
Computation {
program,
witness: Some(witness),
}
}
pub fn without_witness(program: Prog<T>) -> Self {
pub fn without_witness(program: ProgIterator<T, I>) -> Self {
Computation {
program,
witness: None,
@ -79,7 +79,7 @@ fn ark_combination<T: Field + ArkFieldExtensions>(
.fold(LinearCombination::zero(), |acc, e| acc + e)
}
impl<T: Field + ArkFieldExtensions> Prog<T> {
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn generate_constraints(
self,
cs: ConstraintSystemRef<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
@ -148,17 +148,16 @@ impl<T: Field + ArkFieldExtensions> Prog<T> {
}
}
impl<T: Field + ArkFieldExtensions> Computation<T> {
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn prove(self, params: &ProvingKey<T::ArkEngine>) -> Proof<T::ArkEngine> {
let rng = &mut rand_0_7::rngs::StdRng::from_entropy();
let proof = create_random_proof(self.clone(), params, rng).unwrap();
let public_inputs = self.public_inputs_values();
let proof = create_random_proof(self, 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
@ -180,9 +179,9 @@ impl<T: Field + ArkFieldExtensions> Computation<T> {
}
}
impl<T: Field + ArkFieldExtensions>
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
ConstraintSynthesizer<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>
for Computation<T>
for Computation<T, I>
{
fn generate_constraints(
self,

View file

@ -8,7 +8,7 @@ use crate::proof_system::{Backend, NonUniversalBackend, Proof, SetupKeypair};
use zokrates_field::BellmanFieldExtensions;
use zokrates_field::Field;
use crate::ir::{Prog, Witness};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::bellman::Bellman;
use crate::proof_system::bellman::Computation;
use crate::proof_system::bellman::{parse_g1, parse_g2};
@ -18,8 +18,8 @@ use crate::proof_system::Scheme;
const G16_WARNING: &str = "WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/toolbox/proving_schemes.html#g16-malleability for implications.";
impl<T: Field + BellmanFieldExtensions> Backend<T, G16> for Bellman {
fn generate_proof(
program: Prog<T>,
fn generate_proof<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
witness: Witness<T>,
proving_key: Vec<u8>,
) -> Proof<<G16 as Scheme<T>>::ProofPoints> {
@ -28,19 +28,19 @@ impl<T: Field + BellmanFieldExtensions> Backend<T, G16> for Bellman {
let computation = Computation::with_witness(program, witness);
let params = Parameters::read(proving_key.as_slice(), true).unwrap();
let proof = computation.clone().prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
c: parse_g1::<T>(&proof.c),
};
let public_inputs: Vec<String> = computation
.public_inputs_values()
.iter()
.map(|e| format!("0x{}", to_hex(e)))
.collect();
let proof = computation.prove(&params);
let proof_points = ProofPoints {
a: parse_g1::<T>(&proof.a),
b: parse_g2::<T>(&proof.b),
c: parse_g1::<T>(&proof.c),
};
Proof::new(proof_points, public_inputs)
}
@ -84,7 +84,9 @@ impl<T: Field + BellmanFieldExtensions> Backend<T, G16> for Bellman {
}
impl<T: Field + BellmanFieldExtensions> NonUniversalBackend<T, G16> for Bellman {
fn setup(program: Prog<T>) -> SetupKeypair<<G16 as Scheme<T>>::VerificationKey> {
fn setup<I: IntoIterator<Item = Statement<T>>>(
program: ProgIterator<T, I>,
) -> SetupKeypair<<G16 as Scheme<T>>::VerificationKey> {
println!("{}", G16_WARNING);
let parameters = Computation::without_witness(program).setup();
@ -143,22 +145,26 @@ mod tests {
fn verify() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
)],
};
let keypair = <Bellman as NonUniversalBackend<Bn128Field, G16>>::setup(program.clone());
let keypair =
<Bellman as NonUniversalBackend<Bn128Field, G16>>::setup(program.clone().into());
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(42)])
.execute(program.clone().into(), &[Bn128Field::from(42)])
.unwrap();
let proof =
<Bellman as Backend<Bn128Field, G16>>::generate_proof(program, witness, keypair.pk);
let proof = <Bellman as Backend<Bn128Field, G16>>::generate_proof(
program.into(),
witness,
keypair.pk,
);
let ans = <Bellman as Backend<Bn128Field, G16>>::verify(keypair.vk, proof);
assert!(ans);

View file

@ -1,6 +1,6 @@
pub mod groth16;
use crate::ir::{CanonicalLinComb, Prog, Statement, Witness};
use crate::ir::{CanonicalLinComb, ProgIterator, Statement, Witness};
use bellman::groth16::Proof;
use bellman::groth16::{
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof,
@ -20,20 +20,20 @@ pub use self::parse::*;
pub struct Bellman;
#[derive(Clone)]
pub struct Computation<T> {
program: Prog<T>,
pub struct Computation<T, I: IntoIterator<Item = Statement<T>>> {
program: ProgIterator<T, I>,
witness: Option<Witness<T>>,
}
impl<T: Field> Computation<T> {
pub fn with_witness(program: Prog<T>, witness: Witness<T>) -> Self {
impl<T: Field, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn with_witness(program: ProgIterator<T, I>, witness: Witness<T>) -> Self {
Computation {
program,
witness: Some(witness),
}
}
pub fn without_witness(program: Prog<T>) -> Self {
pub fn without_witness(program: ProgIterator<T, I>) -> Self {
Computation {
program,
witness: None,
@ -81,7 +81,7 @@ fn bellman_combination<T: BellmanFieldExtensions, CS: ConstraintSystem<T::Bellma
.fold(LinearCombination::zero(), |acc, e| acc + e)
}
impl<T: BellmanFieldExtensions + Field> Prog<T> {
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn synthesize<CS: ConstraintSystem<T::BellmanEngine>>(
self,
cs: &mut CS,
@ -145,7 +145,7 @@ impl<T: BellmanFieldExtensions + Field> Prog<T> {
}
}
impl<T: BellmanFieldExtensions + Field> Computation<T> {
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
fn get_random_seed(&self) -> Result<[u32; 8], getrandom::Error> {
let mut seed = [0u8; 32];
getrandom::getrandom(&mut seed)?;
@ -163,13 +163,13 @@ impl<T: BellmanFieldExtensions + Field> Computation<T> {
let seed = self.get_random_seed().unwrap();
let rng = &mut ChaChaRng::from_seed(seed.as_ref());
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();
let proof = create_random_proof(self, params, rng).unwrap();
let pvk = prepare_verifying_key(&params.vk);
assert!(verify_proof(&pvk, &proof, &public_inputs).unwrap());
proof
@ -192,7 +192,9 @@ impl<T: BellmanFieldExtensions + Field> Computation<T> {
}
}
impl<T: BellmanFieldExtensions + Field> Circuit<T::BellmanEngine> for Computation<T> {
impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>>
Circuit<T::BellmanEngine> for Computation<T, I>
{
fn synthesize<CS: ConstraintSystem<T::BellmanEngine>>(
self,
cs: &mut CS,
@ -251,6 +253,7 @@ mod tests {
mod prove {
use super::*;
use crate::flat_absy::FlatParameter;
use crate::ir::Prog;
#[test]
fn empty() {
@ -258,7 +261,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter.execute(&program, &[]).unwrap();
let witness = interpreter.execute(program.clone(), &[]).unwrap();
let computation = Computation::with_witness(program, witness);
let params = computation.clone().setup();
@ -269,7 +272,7 @@ mod tests {
fn identity() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -279,7 +282,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(0)])
.execute(program.clone(), &[Bn128Field::from(0)])
.unwrap();
let computation = Computation::with_witness(program, witness);
@ -292,7 +295,7 @@ mod tests {
fn public_identity() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -302,7 +305,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(0)])
.execute(program.clone(), &[Bn128Field::from(0)])
.unwrap();
let computation = Computation::with_witness(program, witness);
@ -315,7 +318,7 @@ mod tests {
fn no_arguments() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::one(),
FlatVariable::public(0),
@ -324,7 +327,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter.execute(&program, &[]).unwrap();
let witness = interpreter.execute(program.clone(), &[]).unwrap();
let computation = Computation::with_witness(program, witness);
let params = computation.clone().setup();
@ -340,7 +343,7 @@ mod tests {
FlatParameter::private(FlatVariable::new(42)),
FlatParameter::public(FlatVariable::new(51)),
],
returns: vec![FlatVariable::public(0), FlatVariable::public(1)],
return_count: 2,
statements: vec![
Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::from(FlatVariable::new(51)),
@ -356,7 +359,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(3), Bn128Field::from(4)])
.execute(program.clone(), &[Bn128Field::from(3), Bn128Field::from(4)])
.unwrap();
let computation = Computation::with_witness(program, witness);
@ -368,7 +371,7 @@ mod tests {
fn one() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(42))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::one(),
FlatVariable::public(0),
@ -378,7 +381,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(3)])
.execute(program.clone(), &[Bn128Field::from(3)])
.unwrap();
let computation = Computation::with_witness(program, witness);
@ -394,7 +397,7 @@ mod tests {
FlatParameter::private(FlatVariable::new(42)),
FlatParameter::public(FlatVariable::new(51)),
],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
LinComb::from(FlatVariable::new(42)) + LinComb::from(FlatVariable::new(51)),
FlatVariable::public(0),
@ -404,7 +407,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(3), Bn128Field::from(4)])
.execute(program.clone(), &[Bn128Field::from(3), Bn128Field::from(4)])
.unwrap();
let computation = Computation::with_witness(program, witness);

View file

@ -1,4 +1,4 @@
use crate::ir::{Prog, Witness};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::gm17::{ProofPoints, VerificationKey, GM17};
use crate::proof_system::libsnark::ffi::{c_free, Buffer, ProofResult, SetupResult};
use crate::proof_system::libsnark::{
@ -39,11 +39,13 @@ extern "C" {
}
impl Backend<Bn128Field, GM17> for Libsnark {
fn generate_proof(
program: Prog<Bn128Field>,
fn generate_proof<I: IntoIterator<Item = Statement<Bn128Field>>>(
program: ProgIterator<Bn128Field, I>,
witness: Witness<Bn128Field>,
proving_key: Vec<u8>,
) -> Proof<<GM17 as Scheme<Bn128Field>>::ProofPoints> {
let program = program.collect();
let (public_inputs_arr, public_inputs_length, private_inputs_arr, private_inputs_length) =
prepare_generate_proof(program.clone(), witness.clone());
@ -130,9 +132,11 @@ impl Backend<Bn128Field, GM17> for Libsnark {
}
impl NonUniversalBackend<Bn128Field, GM17> for Libsnark {
fn setup(
program: Prog<Bn128Field>,
fn setup<I: IntoIterator<Item = Statement<Bn128Field>>>(
program: ProgIterator<Bn128Field, I>,
) -> SetupKeypair<<GM17 as Scheme<Bn128Field>>::VerificationKey> {
let program = program.collect();
let (a_arr, b_arr, c_arr, a_vec, b_vec, c_vec, num_constraints, num_variables, num_inputs) =
prepare_setup(program);
@ -200,7 +204,7 @@ mod tests {
fn verify() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -211,7 +215,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &vec![Bn128Field::from(42)])
.execute(program.clone(), &vec![Bn128Field::from(42)])
.unwrap();
let proof =

View file

@ -230,7 +230,7 @@ pub fn r1cs_program<T: Field>(
// ~out are added after main's arguments, since we want variables (columns)
// in the r1cs to be aligned like "public inputs | private inputs"
let main_return_count = prog.returns.len();
let main_return_count = prog.returns().len();
for i in 0..main_return_count {
provide_variable_idx(&mut variables, &FlatVariable::public(i));

View file

@ -4,7 +4,7 @@ use crate::proof_system::libsnark::{
};
use crate::proof_system::{Backend, G1Affine, G2Affine, NonUniversalBackend, Proof, SetupKeypair};
use crate::ir::{Prog, Witness};
use crate::ir::{ProgIterator, Statement, Witness};
use crate::proof_system::libsnark::serialization::{read_g1, read_g2, write_g1, write_g2};
use crate::proof_system::pghr13::{ProofPoints, VerificationKey, PGHR13};
use crate::proof_system::Scheme;
@ -42,11 +42,13 @@ extern "C" {
}
impl Backend<Bn128Field, PGHR13> for Libsnark {
fn generate_proof(
program: Prog<Bn128Field>,
fn generate_proof<I: IntoIterator<Item = Statement<Bn128Field>>>(
program: ProgIterator<Bn128Field, I>,
witness: Witness<Bn128Field>,
proving_key: Vec<u8>,
) -> Proof<<PGHR13 as Scheme<Bn128Field>>::ProofPoints> {
let program = program.collect();
let (public_inputs_arr, public_inputs_length, private_inputs_arr, private_inputs_length) =
prepare_generate_proof(program.clone(), witness.clone());
@ -156,9 +158,11 @@ impl Backend<Bn128Field, PGHR13> for Libsnark {
}
impl NonUniversalBackend<Bn128Field, PGHR13> for Libsnark {
fn setup(
program: Prog<Bn128Field>,
fn setup<I: IntoIterator<Item = Statement<Bn128Field>>>(
program: ProgIterator<Bn128Field, I>,
) -> SetupKeypair<<PGHR13 as Scheme<Bn128Field>>::VerificationKey> {
let program = program.collect();
let (a_arr, b_arr, c_arr, a_vec, b_vec, c_vec, num_constraints, num_variables, num_inputs) =
prepare_setup(program);
@ -230,7 +234,7 @@ mod tests {
fn verify() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::private(FlatVariable::new(0))],
returns: vec![FlatVariable::public(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::public(0),
@ -241,7 +245,7 @@ mod tests {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &vec![Bn128Field::from(42)])
.execute(program.clone(), &vec![Bn128Field::from(42)])
.unwrap();
let proof =

View file

@ -80,8 +80,8 @@ impl ToString for G2Affine {
}
pub trait Backend<T: Field, S: Scheme<T>> {
fn generate_proof(
program: ir::Prog<T>,
fn generate_proof<I: IntoIterator<Item = ir::Statement<T>>>(
program: ir::ProgIterator<T, I>,
witness: ir::Witness<T>,
proving_key: Vec<u8>,
) -> Proof<S::ProofPoints>;
@ -89,14 +89,16 @@ pub trait Backend<T: Field, S: Scheme<T>> {
fn verify(vk: S::VerificationKey, proof: Proof<S::ProofPoints>) -> bool;
}
pub trait NonUniversalBackend<T: Field, S: NonUniversalScheme<T>>: Backend<T, S> {
fn setup(program: ir::Prog<T>) -> SetupKeypair<S::VerificationKey>;
fn setup<I: IntoIterator<Item = ir::Statement<T>>>(
program: ir::ProgIterator<T, I>,
) -> SetupKeypair<S::VerificationKey>;
}
pub trait UniversalBackend<T: Field, S: UniversalScheme<T>>: Backend<T, S> {
fn universal_setup(size: u32) -> Vec<u8>;
fn setup(
fn setup<I: IntoIterator<Item = ir::Statement<T>>>(
srs: Vec<u8>,
program: ir::Prog<T>,
program: ir::ProgIterator<T, I>,
) -> Result<SetupKeypair<S::VerificationKey>, String>;
}

View file

@ -811,6 +811,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
.get(&import.module_id)
.unwrap()
.functions_iter()
.into_iter()
.filter(|d| d.key.id == import.symbol_id)
.map(|d| DeclarationFunctionKey {
module: import.module_id.to_path_buf(),
@ -1050,6 +1051,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
fn check_single_main(module: &TypedModule<T>) -> Result<(), ErrorInner> {
match module
.functions_iter()
.into_iter()
.filter(|d| d.key.id == "main")
.count()
{

View file

@ -55,13 +55,6 @@ impl<T: Field> PropagateWithContext<T> for FlatExpression<T> {
impl<T: Field> FlatStatement<T> {
fn propagate(self, constants: &mut HashMap<FlatVariable, T>) -> Option<FlatStatement<T>> {
match self {
FlatStatement::Return(list) => Some(FlatStatement::Return(FlatExpressionList {
expressions: list
.expressions
.into_iter()
.map(|e| e.propagate(constants))
.collect(),
})),
FlatStatement::Definition(var, expr) => match expr.propagate(constants) {
FlatExpression::Number(n) => {
constants.insert(var, n);
@ -101,14 +94,6 @@ impl<T: Field> Propagate<T> for FlatFunction<T> {
}
}
impl<T: Field> FlatProg<T> {
pub fn propagate(self) -> FlatProg<T> {
let main = self.main.propagate();
FlatProg { main }
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -14,7 +14,6 @@ mod propagation;
mod reducer;
mod struct_concretizer;
mod uint_optimizer;
mod unconstrained_vars;
mod variable_write_remover;
mod zir_propagation;
@ -26,10 +25,8 @@ use self::propagation::Propagator;
use self::reducer::reduce_program;
use self::struct_concretizer::StructConcretizer;
use self::uint_optimizer::UintOptimizer;
use self::unconstrained_vars::UnconstrainedVariableDetector;
use self::variable_write_remover::VariableWriteRemover;
use crate::compile::CompileConfig;
use crate::ir::Prog;
use crate::static_analysis::constant_resolver::ConstantResolver;
use crate::static_analysis::zir_propagation::ZirPropagator;
use crate::typed_absy::{abi::Abi, TypedProgram};
@ -37,20 +34,12 @@ use crate::zir::ZirProgram;
use std::fmt;
use zokrates_field::Field;
pub trait Analyse {
type Error;
fn analyse(self) -> Result<Self, Self::Error>
where
Self: Sized;
}
#[derive(Debug)]
pub enum Error {
Reducer(self::reducer::Error),
Propagation(self::propagation::Error),
ZirPropagation(self::zir_propagation::Error),
NonConstantArgument(self::constant_argument_checker::Error),
UnconstrainedVariable(self::unconstrained_vars::Error),
OutOfBounds(self::out_of_bounds::Error),
}
@ -84,12 +73,6 @@ impl From<constant_argument_checker::Error> for Error {
}
}
impl From<unconstrained_vars::Error> for Error {
fn from(e: unconstrained_vars::Error) -> Self {
Error::UnconstrainedVariable(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -97,7 +80,6 @@ impl fmt::Display for Error {
Error::Propagation(e) => write!(f, "{}", e),
Error::ZirPropagation(e) => write!(f, "{}", e),
Error::NonConstantArgument(e) => write!(f, "{}", e),
Error::UnconstrainedVariable(e) => write!(f, "{}", e),
Error::OutOfBounds(e) => write!(f, "{}", e),
}
}
@ -176,13 +158,3 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
Ok((zir, abi))
}
}
impl<T: Field> Analyse for Prog<T> {
type Error = Error;
fn analyse(self) -> Result<Self, Self::Error> {
log::debug!("Static analyser: Detect unconstrained zir");
UnconstrainedVariableDetector::detect(&self).map_err(Error::from)?;
Ok(self)
}
}

View file

@ -1,164 +0,0 @@
use crate::flat_absy::{FlatParameter, FlatVariable};
use crate::ir::visitor::Visitor;
use crate::ir::Directive;
use crate::ir::Prog;
use std::collections::HashSet;
use std::fmt;
use zokrates_field::Field;
#[derive(Debug, Default)]
pub struct UnconstrainedVariableDetector {
pub(self) variables: HashSet<FlatVariable>,
}
#[derive(Debug, PartialEq)]
pub struct Error(usize);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Found unconstrained variables during IR analysis (found {} occurrence{}). If this is intentional, use the `--allow-unconstrained-variables` flag.",
self.0,
if self.0 == 1 { "" } else { "s" }
)
}
}
impl UnconstrainedVariableDetector {
pub fn detect<T: Field>(p: &Prog<T>) -> Result<(), Error> {
let mut instance = Self::default();
instance.visit_module(p);
if instance.variables.is_empty() {
Ok(())
} else {
Err(Error(instance.variables.len()))
}
}
}
impl<T: Field> Visitor<T> for UnconstrainedVariableDetector {
fn visit_argument(&mut self, p: &FlatParameter) {
if p.private {
self.variables.insert(p.id);
}
}
fn visit_variable(&mut self, v: &FlatVariable) {
self.variables.remove(v);
}
fn visit_directive(&mut self, d: &Directive<T>) {
self.variables.extend(d.outputs.iter());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::flat_absy::FlatVariable;
use crate::ir::{LinComb, Prog, QuadComb, Statement};
use crate::solvers::Solver;
use zokrates_field::Bn128Field;
#[test]
fn unconstrained_private_input() {
// def main(_0) -> (1):
// (1 * ~one) * (42 * ~one) == 1 * ~out_0
// return ~out_0
let _0 = FlatParameter::private(FlatVariable::new(0)); // unused private parameter
let one = FlatVariable::one();
let out_0 = FlatVariable::public(0);
let p: Prog<Bn128Field> = Prog {
arguments: vec![_0],
statements: vec![Statement::constraint(
QuadComb::from_linear_combinations(
LinComb::summand(1, one),
LinComb::summand(42, one),
),
LinComb::summand(1, out_0),
)],
returns: vec![out_0],
};
let result = UnconstrainedVariableDetector::detect(&p);
assert_eq!(
result.expect_err("expected an error").to_string(),
"Found unconstrained variables during IR analysis (found 1 occurrence). If this is intentional, use the `--allow-unconstrained-variables` flag."
);
}
#[test]
fn constrained_private_input() {
// def main(_0) -> (1):
// (1 * ~one) * (1 * _0) == 1 * ~out_0
// return ~out_0
let _0 = FlatParameter::private(FlatVariable::new(0));
let out_0 = FlatVariable::public(0);
let p: Prog<Bn128Field> = Prog {
arguments: vec![_0],
statements: vec![Statement::definition(out_0, LinComb::from(_0.id))],
returns: vec![out_0],
};
let result = UnconstrainedVariableDetector::detect(&p);
assert_eq!(result, Ok(()));
}
#[test]
fn constrained_directive() {
// def main(_0) -> (1):
// # _1, _2 = ConditionEq((-42) * ~one + 1 * _0)
// ((-42) * ~one + 1 * _0) * (1 * _2) == 1 * _1
// (1 * ~one + (-1) * _1) * ((-42) * ~one + 1 * _0) == 0
// (1 * ~one) * (1 * ~one + (-1) * _1) == 1 * ~out_0
// return ~out_0
let v_0 = FlatParameter::private(FlatVariable::new(0));
let v_1 = FlatVariable::new(1);
let v_2 = FlatVariable::new(2);
let out_0 = FlatVariable::public(0);
let one = FlatVariable::one();
let p: Prog<Bn128Field> = Prog {
arguments: vec![v_0],
statements: vec![
Statement::Directive(Directive {
inputs: vec![(LinComb::summand(-42, one) + LinComb::summand(1, v_0.id)).into()],
outputs: vec![v_1, v_2],
solver: Solver::ConditionEq,
}),
Statement::constraint(
QuadComb::from_linear_combinations(
LinComb::summand(-42, one) + LinComb::summand(1, v_0.id),
LinComb::summand(1, v_2),
),
LinComb::summand(1, v_1),
),
Statement::constraint(
QuadComb::from_linear_combinations(
LinComb::summand(1, one) + LinComb::summand(-1, v_1),
LinComb::summand(-42, one) + LinComb::summand(1, v_0.id),
),
LinComb::zero(),
),
Statement::constraint(
QuadComb::from_linear_combinations(
LinComb::summand(1, one),
LinComb::summand(1, one) + LinComb::summand(-1, v_1),
),
LinComb::summand(1, out_0),
),
],
returns: vec![out_0],
};
let result = UnconstrainedVariableDetector::detect(&p);
assert_eq!(result, Ok(()));
}
}

View file

@ -3,6 +3,7 @@ extern crate zokrates_core;
extern crate zokrates_field;
use std::io;
use typed_arena::Arena;
use zokrates_common::Resolver;
use zokrates_core::compile::CompileConfig;
use zokrates_core::{
@ -26,11 +27,14 @@ fn lt_field() {
// the fact that `2*10000 - 2*5555` has two distinct bit decompositions
// we chose the one which is out of range, ie the sum check features an overflow
let res: CompilationArtifacts<Bn128Field> = compile(
let arena = Arena::new();
let res: CompilationArtifacts<Bn128Field, _> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
&CompileConfig::default(),
CompileConfig::default(),
&arena,
)
.unwrap();
@ -58,11 +62,14 @@ fn lt_uint() {
// the fact that `2*10000 - 2*5555` has two distinct bit decompositions
// we chose the one which is out of range, ie the sum check features an overflow
let res: CompilationArtifacts<Bn128Field> = compile(
let arena = Arena::new();
let res: CompilationArtifacts<Bn128Field, _> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
&CompileConfig::default(),
CompileConfig::default(),
&arena,
)
.unwrap();
@ -99,13 +106,16 @@ fn unpack256() {
)
.unwrap();
let res: CompilationArtifacts<Bn128Field> = compile(
let arena = Arena::new();
let res: CompilationArtifacts<Bn128Field, _> = compile(
source,
"./path/to/file".into(),
Some(&FileSystemResolver::with_stdlib_root(
stdlib_path.to_str().unwrap(),
)),
&CompileConfig::default(),
CompileConfig::default(),
&arena,
)
.unwrap();
@ -139,13 +149,16 @@ fn unpack256_unchecked() {
)
.unwrap();
let res: CompilationArtifacts<Bn128Field> = compile(
let arena = Arena::new();
let res: CompilationArtifacts<Bn128Field, _> = compile(
source,
"./path/to/file".into(),
Some(&FileSystemResolver::with_stdlib_root(
stdlib_path.to_str().unwrap(),
)),
&CompileConfig::default(),
CompileConfig::default(),
&arena,
)
.unwrap();

View file

@ -16,7 +16,7 @@ use zokrates_core::proof_system::groth16::G16;
fn generate_proof() {
let program: Prog<Bn128Field> = Prog {
arguments: vec![FlatParameter::public(FlatVariable::new(0))],
returns: vec![FlatVariable::new(0)],
return_count: 1,
statements: vec![Statement::constraint(
FlatVariable::new(0),
FlatVariable::new(0),
@ -25,7 +25,7 @@ fn generate_proof() {
let interpreter = Interpreter::default();
let witness = interpreter
.execute(&program, &[Bn128Field::from(42)])
.execute(program.clone(), &[Bn128Field::from(42)])
.unwrap();
let keypair = <Bellman as NonUniversalBackend<Bn128Field, G16>>::setup(program.clone());

View file

@ -2,7 +2,6 @@
"entry_point": "./tests/tests/panics/deep_branch.zok",
"curves": ["Bn128"],
"config": {
"allow_unconstrained_variables": false,
"isolate_branches": true
},
"tests": [

View file

@ -2,7 +2,6 @@
"entry_point": "./tests/tests/panics/internal_panic.zok",
"curves": ["Bn128"],
"config": {
"allow_unconstrained_variables": false,
"isolate_branches": true
},
"tests": [

View file

@ -1,7 +1,6 @@
{
"entry_point": "./tests/tests/panics/panic_isolation.zok",
"config": {
"allow_unconstrained_variables": false,
"isolate_branches": true
},
"curves": ["Bn128"],

View file

@ -1,7 +1,6 @@
{
"entry_point": "./tests/tests/panics/panic_isolation.zok",
"config": {
"allow_unconstrained_variables": false,
"isolate_branches": false
},
"curves": ["Bn128"],

View file

@ -54,7 +54,10 @@ impl fmt::Debug for FieldParseError {
}
pub trait Field:
From<i32>
'static
+ Sync
+ Send
+ From<i32>
+ From<u8>
+ From<u32>
+ From<usize>

19
zokrates_js/Cargo.lock generated
View file

@ -701,6 +701,12 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -1215,6 +1221,16 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
@ -1558,7 +1574,6 @@ name = "zokrates_core"
version = "0.6.7"
dependencies = [
"bellman_ce",
"bincode",
"cfg-if 0.1.10",
"csv",
"ff_ce 0.9.0",
@ -1574,6 +1589,7 @@ dependencies = [
"reduce",
"regex",
"serde",
"serde_cbor",
"serde_json",
"typed-arena",
"zokrates_common",
@ -1624,6 +1640,7 @@ dependencies = [
"js-sys",
"serde",
"serde_json",
"typed-arena",
"wasm-bindgen",
"zokrates_abi",
"zokrates_common",

View file

@ -12,6 +12,7 @@ js-sys = "0.3.33"
serde = { version = "^1.0.59", features = ["derive"] }
serde_json = "1.0"
wasm-bindgen = { version = "0.2.46", features = ["serde-serialize"] }
typed-arena = "1.4.1"
zokrates_core = { path = "../zokrates_core", features = ["wasm", "bellman"], default-features = false }
zokrates_common = { path = "../zokrates_common" }
zokrates_field = { path = "../zokrates_field", default-features = false, features = ["bellman"] }

View file

@ -10,7 +10,6 @@ declare module 'zokrates-js' {
export type ResolveCallback = (location: string, path: string) => ResolverResult;
export interface CompileConfig {
allow_unconstrained_variables?: boolean,
isolate_branches?: boolean
}

View file

@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
use serde_json::to_string_pretty;
use std::io::Cursor;
use std::path::PathBuf;
use typed_arena::Arena;
use wasm_bindgen::prelude::*;
use zokrates_abi::{parse_strict, Decode, Encode, Inputs};
use zokrates_common::Resolver;
@ -41,13 +42,15 @@ pub struct ComputationResult {
fn deserialize_program(value: &[u8]) -> Result<ir::Prog<Bn128Field>, JsValue> {
let prog = ir::ProgEnum::deserialize(value).map_err(|err| JsValue::from_str(&err))?;
match prog {
ir::ProgEnum::Bn128Program(p) => Ok(p),
ir::ProgEnum::Bn128Program(p) => Ok(p.collect()),
_ => Err(JsValue::from_str("Unsupported binary")),
}
}
#[inline]
fn serialize_program(program: &ir::Prog<Bn128Field>) -> Vec<u8> {
fn serialize_program<I: IntoIterator<Item = ir::Statement<Bn128Field>>>(
program: ir::ProgIterator<Bn128Field, I>,
) -> Vec<u8> {
let mut buffer = Cursor::new(vec![]);
program.serialize(&mut buffer);
buffer.into_inner()
@ -106,11 +109,15 @@ pub fn compile(
let config: CompileConfig = config.into_serde().unwrap_or_default();
let fmt_error = |e: &CompileError| format!("{}:{}", e.file().display(), e.value());
let artifacts: CompilationArtifacts<Bn128Field> = core_compile(
let arena = Arena::new();
let artifacts: CompilationArtifacts<Bn128Field, _> = core_compile(
source.as_string().unwrap(),
PathBuf::from(location.as_string().unwrap()),
Some(&resolver),
&config,
config,
&arena,
)
.map_err(|ce| {
JsValue::from_str(
@ -123,8 +130,8 @@ pub fn compile(
})?;
let result = CompilationResult {
program: serialize_program(artifacts.prog()),
abi: to_string_pretty(artifacts.abi()).unwrap(),
program: serialize_program(artifacts.prog()),
};
Ok(JsValue::from_serde(&result).unwrap())
@ -146,7 +153,7 @@ pub fn compute_witness(program: &[u8], abi: JsValue, args: JsValue) -> Result<Js
let interpreter = ir::Interpreter::default();
let witness = interpreter
.execute(&program_flattened, &inputs.encode())
.execute(program_flattened, &inputs.encode())
.map_err(|err| JsValue::from_str(&format!("Execution failed: {}", err)))?;
let return_values: serde_json::Value =

View file

@ -1318,7 +1318,7 @@ mod tests {
span: Span::new(source, 76, 77).unwrap()
}
))),
Span::new(&source, 59, 80).unwrap()
Span::new(source, 59, 80).unwrap()
)],
span: Span::new(source, 52, 80).unwrap(),
})],

View file

@ -12,5 +12,6 @@ zokrates_abi = { version = "0.1", path = "../zokrates_abi" }
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
typed-arena = "1.4.1"
[lib]

View file

@ -149,10 +149,14 @@ fn compile_and_run<T: Field>(t: Tests) {
let stdlib = std::fs::canonicalize("../zokrates_stdlib/stdlib").unwrap();
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
let artifacts = compile::<T, _>(code, entry_point.clone(), Some(&resolver), &config).unwrap();
let arena = typed_arena::Arena::new();
let bin = artifacts.prog();
let abi = artifacts.abi();
let artifacts =
compile::<T, _>(code, entry_point.clone(), Some(&resolver), config, &arena).unwrap();
let (bin, abi) = artifacts.into_inner();
// here we do want the program in memory because we want to run many tests on it
let bin = bin.collect();
if let Some(target_count) = t.max_constraint_count {
let count = bin.constraint_count();
@ -184,7 +188,7 @@ fn compile_and_run<T: Field>(t: Tests) {
.unwrap()
};
let output = interpreter.execute(bin, &input);
let output = interpreter.execute(bin.clone(), &input);
if let Err(e) = compare(output, test.output) {
let mut code = File::open(&entry_point).unwrap();