Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce compilation mode #988

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/unreleased/988-schaeff
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Introduce a check mode flag `--mode` and stop requiring a main function in `lib` mode
5 changes: 5 additions & 0 deletions zokrates_cli/examples/compile_errors/two_entry_points.zok
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def main():
return

def main(field a):
return
6 changes: 3 additions & 3 deletions zokrates_cli/src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ mod tests {
use std::fs::File;
use std::io::{BufReader, Read};
use std::string::String;
use zokrates_core::compile::{compile, CompilationArtifacts, CompileConfig};
use zokrates_core::compile::{compile, BinCompilationArtifacts, CompileConfig};
use zokrates_core::ir;
use zokrates_field::Bn128Field;
use zokrates_fs_resolver::FileSystemResolver;
Expand Down Expand Up @@ -189,7 +189,7 @@ 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> =
let artifacts: BinCompilationArtifacts<ir::Prog<Bn128Field>> =
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();

let interpreter = ir::Interpreter::default();
Expand Down Expand Up @@ -221,7 +221,7 @@ 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> =
let artifacts: BinCompilationArtifacts<ir::Prog<Bn128Field>> =
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();

let interpreter = ir::Interpreter::default();
Expand Down
4 changes: 4 additions & 0 deletions zokrates_cli/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ pub const UNIVERSAL_SETUP_DEFAULT_PATH: &str = "universal_setup.dat";
pub const UNIVERSAL_SETUP_DEFAULT_SIZE: &str = "10";
pub const SMTLIB2_DEFAULT_PATH: &str = "out.smt2";

pub const COMPILATION_MODE_BIN: &str = "bin";
pub const COMPILATION_MODE_LIB: &str = "lib";
pub const COMPILATION_MODES: &[&str] = &[COMPILATION_MODE_BIN, COMPILATION_MODE_LIB];

pub const BELLMAN: &str = "bellman";
pub const LIBSNARK: &str = "libsnark";
pub const ARK: &str = "ark";
Expand Down
20 changes: 17 additions & 3 deletions zokrates_cli/src/ops/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::convert::TryFrom;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};
use zokrates_core::compile::{check, CompileConfig, CompileError};
use zokrates_core::compile::{check, CompileConfig, CompileError, CompileMode};
use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field};
use zokrates_fs_resolver::FileSystemResolver;

Expand Down Expand Up @@ -46,6 +46,13 @@ pub fn subcommand() -> App<'static, 'static> {
.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("mode")
.long("mode")
.help("In which mode the compiler should be run")
.possible_values(constants::COMPILATION_MODES)
.required(false)
.default_value(constants::COMPILATION_MODE_BIN)
)
}

pub fn exec(sub_matches: &ArgMatches) -> Result<(), String> {
Expand Down Expand Up @@ -89,8 +96,15 @@ fn cli_check<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
)),
}?;

let config =
CompileConfig::default().isolate_branches(sub_matches.is_present("isolate-branches"));
let mode = match sub_matches.value_of("mode").unwrap() {
constants::COMPILATION_MODE_BIN => CompileMode::Bin,
constants::COMPILATION_MODE_LIB => CompileMode::Lib,
_ => unreachable!(),
};

let config = CompileConfig::default()
.isolate_branches(sub_matches.is_present("isolate-branches"))
.mode(mode);

let resolver = FileSystemResolver::with_stdlib_root(stdlib_path);
let _ = check::<T, _>(source, path, Some(&resolver), &config).map_err(|e| {
Expand Down
12 changes: 8 additions & 4 deletions zokrates_cli/src/ops/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use std::convert::TryFrom;
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use zokrates_core::compile::{compile, CompilationArtifacts, CompileConfig, CompileError};
use zokrates_core::{
compile::{compile, BinCompilationArtifacts, CompileConfig, CompileError, CompileMode},
ir::Prog,
};
use zokrates_field::{Bls12_377Field, Bls12_381Field, Bn128Field, Bw6_761Field, Field};
use zokrates_fs_resolver::FileSystemResolver;

Expand Down Expand Up @@ -130,14 +133,15 @@ 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"));
.isolate_branches(sub_matches.is_present("isolate-branches"))
.mode(CompileMode::Bin);

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 artifacts: BinCompilationArtifacts<Prog<T>> =
compile(source, path, Some(&resolver), &config).map_err(|e| {
format!(
"Compilation failed:\n\n{}",
e.0.iter()
Expand Down
127 changes: 84 additions & 43 deletions zokrates_core/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ use zokrates_field::Field;
use zokrates_pest_ast as pest;

#[derive(Debug)]
pub struct CompilationArtifacts<T: Field> {
prog: ir::Prog<T>,
pub struct BinCompilationArtifacts<P> {
prog: P,
abi: Abi,
}

impl<T: Field> CompilationArtifacts<T> {
pub fn prog(&self) -> &ir::Prog<T> {
#[derive(Debug)]
pub enum CompilationArtifacts<P> {
Bin(BinCompilationArtifacts<P>),
Lib,
}

impl<P> BinCompilationArtifacts<P> {
pub fn prog(&self) -> &P {
&self.prog
}

Expand Down Expand Up @@ -162,9 +168,25 @@ impl fmt::Display for CompileErrorInner {
}
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum CompileMode {
Bin,
Copy link
Member

@dark64 dark64 Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use something like this here

#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum CompileMode {
    #[serde(rename = "bin")]
    Bin,
    #[serde(rename = "lib")]
    Lib
}

so it's a bit cleaner in js: mode: "lib" instead of mode: "Lib". It's also possible to put #[serde(rename_all = "lowercase")] before the enum definition.

We should also update typescript type information file:
https://github.com/Zokrates/ZoKrates/blob/add-compiler-mode-flag/zokrates_js/index.d.ts#L12

export type CompileMode = "bin" | "lib";

export interface CompileConfig {
   allow_unconstrained_variables?: boolean,
   isolate_branches?: boolean,
   mode?: CompileMode
}

Lib,
}

impl Default for CompileMode {
fn default() -> Self {
Self::Bin
}
}

#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct CompileConfig {
#[serde(default)]
pub mode: CompileMode,
#[serde(default)]
pub allow_unconstrained_variables: bool,
#[serde(default)]
pub isolate_branches: bool,
}

Expand All @@ -177,6 +199,10 @@ impl CompileConfig {
self.isolate_branches = flag;
self
}
pub fn mode(mut self, mode: CompileMode) -> Self {
self.mode = mode;
self
}
}

type FilePath = PathBuf;
Expand All @@ -186,37 +212,42 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
location: FilePath,
resolver: Option<&dyn Resolver<E>>,
config: &CompileConfig,
) -> Result<CompilationArtifacts<T>, CompileErrors> {
) -> Result<BinCompilationArtifacts<ir::Prog<T>>, CompileErrors> {
let arena = Arena::new();

let (typed_ast, abi) = check_with_arena(source, location.clone(), resolver, config, &arena)?;

// flatten input program
log::debug!("Flatten");
let program_flattened = Flattener::flatten(typed_ast, config);

// constant propagation after call resolution
log::debug!("Propagate flat program");
let program_flattened = program_flattened.propagate();

// convert to ir
log::debug!("Convert to IR");
let ir_prog = ir::Prog::from(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,
})
let artifacts = check_with_arena(source, location.clone(), resolver, config, &arena)?;

match artifacts {
CompilationArtifacts::Lib => unreachable!(),
Copy link
Member

@dark64 dark64 Nov 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be reachable when called from zokrates-js because the config is passed by the caller:

https://github.com/Zokrates/ZoKrates/pull/988/files#diff-31d8ede13068a8e0e79ec05ff89244788c8903841d377659d712b5d2057e9ea6R109

If my understanding is correct, one could set the mode to lib and call compile which would throw here with no clear message.

CompilationArtifacts::Bin(artifacts) => {
// flatten input program
log::debug!("Flatten");
let program_flattened = Flattener::flatten(artifacts.prog, config);

// constant propagation after call resolution
log::debug!("Propagate flat program");
let program_flattened = program_flattened.propagate();

// convert to ir
log::debug!("Convert to IR");
let ir_prog = ir::Prog::from(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(BinCompilationArtifacts {
prog: optimized_ir_prog,
abi: artifacts.abi,
})
}
}
}

pub fn check<T: Field, E: Into<imports::Error>>(
Expand All @@ -236,7 +267,7 @@ fn check_with_arena<'ast, T: Field, E: Into<imports::Error>>(
resolver: Option<&dyn Resolver<E>>,
config: &CompileConfig,
arena: &'ast Arena<String>,
) -> Result<(ZirProgram<'ast, T>, Abi), CompileErrors> {
) -> Result<CompilationArtifacts<ZirProgram<'ast, T>>, CompileErrors> {
let source = arena.alloc(source);

log::debug!("Parse program with entry file {}", location.display());
Expand All @@ -246,17 +277,27 @@ fn check_with_arena<'ast, T: Field, E: Into<imports::Error>>(
log::debug!("Check semantics");

// check semantics
let typed_ast = Checker::check(compiled)
let typed_ast = Checker::check(compiled, config.mode)
.map_err(|errors| CompileErrors(errors.into_iter().map(CompileError::from).collect()))?;

let main_module = typed_ast.main.clone();
match config.mode {
CompileMode::Bin => {
let main_module = typed_ast.main.clone();

log::debug!("Run static analysis");

log::debug!("Run static analysis");
// analyse (unroll and constant propagation)
let (prog, abi) = typed_ast.analyse(config).map_err(|e| {
CompileErrors(vec![CompileErrorInner::from(e).in_file(&main_module)])
})?;

// analyse (unroll and constant propagation)
typed_ast
.analyse(config)
.map_err(|e| CompileErrors(vec![CompileErrorInner::from(e).in_file(&main_module)]))
Ok(CompilationArtifacts::Bin(BinCompilationArtifacts {
prog,
abi,
}))
}
CompileMode::Lib => Ok(CompilationArtifacts::Lib),
}
}

pub fn parse_program<'ast, T: Field, E: Into<imports::Error>>(
Expand Down Expand Up @@ -322,7 +363,7 @@ mod test {
return foo()
"#
.to_string();
let res: Result<CompilationArtifacts<Bn128Field>, CompileErrors> = compile(
let res: Result<BinCompilationArtifacts<ir::Prog<Bn128Field>>, CompileErrors> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
Expand All @@ -341,7 +382,7 @@ mod test {
return 1
"#
.to_string();
let res: Result<CompilationArtifacts<Bn128Field>, CompileErrors> = compile(
let res: Result<BinCompilationArtifacts<ir::Prog<Bn128Field>>, CompileErrors> = compile(
source,
"./path/to/file".into(),
None::<&dyn Resolver<io::Error>>,
Expand Down
Loading