merge dev
This commit is contained in:
commit
fcec50f614
13 changed files with 185 additions and 36 deletions
|
@ -1,2 +1,5 @@
|
|||
struct Bar {
|
||||
}
|
||||
|
||||
def main() -> (field):
|
||||
return 21
|
|
@ -1,2 +1,5 @@
|
|||
struct Baz {
|
||||
}
|
||||
|
||||
def main() -> (field):
|
||||
return 123
|
|
@ -1,4 +1,9 @@
|
|||
from "./baz" import Baz
|
||||
|
||||
import "./baz"
|
||||
from "./baz" import main as my_function
|
||||
|
||||
def main() -> (field):
|
||||
field a = my_function()
|
||||
Baz b = Baz {}
|
||||
return baz()
|
|
@ -1,5 +1,11 @@
|
|||
from "./bar" import Bar as MyBar
|
||||
from "./bar" import Bar
|
||||
|
||||
import "./foo"
|
||||
import "./bar"
|
||||
|
||||
def main() -> (field):
|
||||
MyBar my_bar = MyBar {}
|
||||
Bar bar = Bar {}
|
||||
my_bar == bar
|
||||
return foo() + bar()
|
|
@ -15,7 +15,7 @@ use std::io::{stdin, BufReader, BufWriter, Read, Write};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::string::String;
|
||||
use zokrates_abi::Encode;
|
||||
use zokrates_core::compile::{check, compile, CompilationArtifacts, CompileError};
|
||||
use zokrates_core::compile::{check, compile, CompilationArtifacts, CompileConfig, CompileError};
|
||||
use zokrates_core::ir::{self, ProgEnum};
|
||||
use zokrates_core::proof_system::*;
|
||||
use zokrates_core::typed_absy::abi::Abi;
|
||||
|
@ -260,6 +260,8 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
|
|||
|
||||
let hr_output_path = bin_output_path.to_path_buf().with_extension("ztf");
|
||||
|
||||
let is_release = sub_matches.occurrences_of("release") > 0;
|
||||
|
||||
let file = File::open(path.clone())
|
||||
.map_err(|why| format!("Couldn't open input file {}: {}", path.display(), why))?;
|
||||
|
||||
|
@ -282,8 +284,10 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
|
|||
|
||||
let resolver =
|
||||
FileSystemResolver::with_stdlib_root(sub_matches.value_of("stdlib-path").unwrap());
|
||||
|
||||
let compilation_config = CompileConfig::default().with_is_release(is_release);
|
||||
let artifacts: CompilationArtifacts<T> =
|
||||
compile(source, path, Some(&resolver)).map_err(|e| {
|
||||
compile(source, path, Some(&resolver), &compilation_config).map_err(|e| {
|
||||
format!(
|
||||
"Compilation failed:\n\n{}",
|
||||
e.0.iter()
|
||||
|
@ -414,7 +418,6 @@ fn cli() -> Result<(), String> {
|
|||
const VERIFICATION_CONTRACT_DEFAULT_PATH: &str = "verifier.sol";
|
||||
const WITNESS_DEFAULT_PATH: &str = "witness";
|
||||
const JSON_PROOF_PATH: &str = "proof.json";
|
||||
|
||||
let default_curve = env::var("ZOKRATES_CURVE").unwrap_or(constants::BN128.into());
|
||||
let default_scheme = env::var("ZOKRATES_PROVING_SCHEME").unwrap_or(constants::G16.into());
|
||||
let default_solidity_abi = "v1";
|
||||
|
@ -469,6 +472,10 @@ fn cli() -> Result<(), String> {
|
|||
.long("light")
|
||||
.help("Skip logs and human readable output")
|
||||
.required(false)
|
||||
).arg(Arg::with_name("release")
|
||||
.long("release")
|
||||
.help("Apply release optimisations to minimise constraint count. This increases compilation time.")
|
||||
.required(false)
|
||||
)
|
||||
)
|
||||
.subcommand(SubCommand::with_name("check")
|
||||
|
@ -951,7 +958,7 @@ mod tests {
|
|||
let stdlib = std::fs::canonicalize("../zokrates_stdlib/stdlib").unwrap();
|
||||
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
|
||||
let _: CompilationArtifacts<Bn128Field> =
|
||||
compile(source, path, Some(&resolver)).unwrap();
|
||||
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,7 +982,7 @@ mod tests {
|
|||
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
|
||||
|
||||
let artifacts: CompilationArtifacts<Bn128Field> =
|
||||
compile(source, path, Some(&resolver)).unwrap();
|
||||
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();
|
||||
|
||||
let interpreter = ir::Interpreter::default();
|
||||
|
||||
|
@ -1006,7 +1013,7 @@ mod tests {
|
|||
let resolver = FileSystemResolver::with_stdlib_root(stdlib.to_str().unwrap());
|
||||
|
||||
let artifacts: CompilationArtifacts<Bn128Field> =
|
||||
compile(source, path, Some(&resolver)).unwrap();
|
||||
compile(source, path, Some(&resolver), &CompileConfig::default()).unwrap();
|
||||
|
||||
let interpreter = ir::Interpreter::default();
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ use imports::{self, Importer};
|
|||
use ir;
|
||||
use macros;
|
||||
use macros::process_macros;
|
||||
use optimizer::Optimize;
|
||||
use semantics::{self, Checker};
|
||||
use static_analysis::Analyse;
|
||||
use std::collections::HashMap;
|
||||
|
@ -141,12 +140,29 @@ impl fmt::Display for CompileErrorInner {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct CompileConfig {
|
||||
is_release: bool,
|
||||
}
|
||||
|
||||
impl CompileConfig {
|
||||
pub fn with_is_release(mut self, is_release: bool) -> Self {
|
||||
self.is_release = is_release;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_release(&self) -> bool {
|
||||
self.is_release
|
||||
}
|
||||
}
|
||||
|
||||
type FilePath = PathBuf;
|
||||
|
||||
pub fn compile<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();
|
||||
|
||||
|
@ -164,7 +180,7 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
|
|||
let ir_prog = ir::Prog::from(program_flattened);
|
||||
|
||||
// optimize
|
||||
let optimized_ir_prog = ir_prog.optimize();
|
||||
let optimized_ir_prog = ir_prog.optimize(config);
|
||||
|
||||
// analyse (check for unused constraints)
|
||||
let optimized_ir_prog = optimized_ir_prog.analyse();
|
||||
|
@ -264,6 +280,7 @@ mod test {
|
|||
source,
|
||||
"./path/to/file".into(),
|
||||
None::<&dyn Resolver<io::Error>>,
|
||||
&CompileConfig::default(),
|
||||
);
|
||||
assert!(res.unwrap_err().0[0]
|
||||
.value()
|
||||
|
@ -282,6 +299,7 @@ mod test {
|
|||
source,
|
||||
"./path/to/file".into(),
|
||||
None::<&dyn Resolver<io::Error>>,
|
||||
&CompileConfig::default(),
|
||||
);
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
@ -362,6 +380,7 @@ struct Bar { field a }
|
|||
main.to_string(),
|
||||
"main".into(),
|
||||
Some(&CustomResolver),
|
||||
&CompileConfig::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -79,6 +79,27 @@ impl<T: Field> Eq for LinComb<T> {}
|
|||
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct CanonicalLinComb<T>(pub BTreeMap<FlatVariable, T>);
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash, Debug, Serialize, Deserialize)]
|
||||
pub struct CanonicalQuadComb<T> {
|
||||
left: CanonicalLinComb<T>,
|
||||
right: CanonicalLinComb<T>,
|
||||
}
|
||||
|
||||
impl<T> From<CanonicalQuadComb<T>> for QuadComb<T> {
|
||||
fn from(q: CanonicalQuadComb<T>) -> Self {
|
||||
QuadComb {
|
||||
left: q.left.into(),
|
||||
right: q.right.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<CanonicalLinComb<T>> for LinComb<T> {
|
||||
fn from(l: CanonicalLinComb<T>) -> Self {
|
||||
LinComb(l.0.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LinComb<T> {
|
||||
pub fn summand<U: Into<T>>(mult: U, var: FlatVariable) -> LinComb<T> {
|
||||
let res = vec![(var, mult.into())];
|
||||
|
@ -160,6 +181,15 @@ impl<T: Field> LinComb<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Field> QuadComb<T> {
|
||||
pub fn as_canonical(&self) -> CanonicalQuadComb<T> {
|
||||
CanonicalQuadComb {
|
||||
left: self.left.as_canonical(),
|
||||
right: self.right.as_canonical(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Field> fmt::Display for LinComb<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.is_zero() {
|
||||
|
|
|
@ -133,7 +133,7 @@ impl Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
fn execute_solver<T: Field>(&self, s: &Solver, inputs: &Vec<T>) -> Result<Vec<T>, String> {
|
||||
pub fn execute_solver<T: Field>(&self, s: &Solver, inputs: &Vec<T>) -> Result<Vec<T>, String> {
|
||||
use solvers::Signed;
|
||||
let (expected_input_count, expected_output_count) = s.get_signature();
|
||||
assert!(inputs.len() == expected_input_count);
|
||||
|
|
|
@ -11,18 +11,19 @@ mod tautology;
|
|||
use self::duplicate::DuplicateOptimizer;
|
||||
use self::redefinition::RedefinitionOptimizer;
|
||||
use self::tautology::TautologyOptimizer;
|
||||
use compile::CompileConfig;
|
||||
|
||||
use crate::ir::Prog;
|
||||
use zokrates_field::Field;
|
||||
|
||||
pub trait Optimize {
|
||||
fn optimize(self) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Field> Optimize for Prog<T> {
|
||||
fn optimize(self) -> Self {
|
||||
// remove redefinitions
|
||||
let r = RedefinitionOptimizer::optimize(self);
|
||||
impl<T: Field> Prog<T> {
|
||||
pub fn optimize(self, config: &CompileConfig) -> Self {
|
||||
let r = if config.is_release() {
|
||||
// remove redefinitions
|
||||
RedefinitionOptimizer::optimize(self)
|
||||
} else {
|
||||
self
|
||||
};
|
||||
// remove constraints that are always satisfied
|
||||
let r = TautologyOptimizer::optimize(r);
|
||||
// remove duplicate constraints
|
||||
|
|
|
@ -23,11 +23,14 @@
|
|||
// ## Optimization rules
|
||||
|
||||
// We maintain `s`, a set of substitutions as a mapping of `(variable => linear_combination)`. It starts empty.
|
||||
// We also maintaint `i`, a set of variables that should be ignored when trying to substitute. It starts empty.
|
||||
// We also maintain `i`, a set of variables that should be ignored when trying to substitute. It starts empty.
|
||||
|
||||
// - input variables are inserted into `i`
|
||||
// - the `~one` variable is inserted into `i`
|
||||
// - For each directive, for each variable `v` introduced, insert `v` into `i`
|
||||
// - For each directive
|
||||
// - if all directive inputs are of the form as `coeff * ~one`, execute the solver with all `coeff` as inputs, introducing `res`
|
||||
// as the output, and insert `(v, o)` into `s` for `(v, o)` in `(d.outputs, res)`
|
||||
// - else, for each variable `v` introduced, insert `v` into `i`
|
||||
// - For each constraint `c`, we replace all variables by their value in `s` if any, otherwise leave them unchanged. Let's call `c_0` the resulting constraint. We either return `c_0` or nothing based on the form of `c_0`:
|
||||
// - `~one * lin == k * v if v isn't in i`: insert `(v, lin / k)` into `s` and return nothing
|
||||
// - `q == k * v if v isn't in i`: insert `v` into `i` and return `c_0`
|
||||
|
@ -114,25 +117,86 @@ impl<T: Field> Folder<T> for RedefinitionOptimizer<T> {
|
|||
}
|
||||
Statement::Directive(d) => {
|
||||
let d = self.fold_directive(d);
|
||||
// to prevent the optimiser from replacing variables introduced by directives, add them to the ignored set
|
||||
for o in d.outputs.iter().cloned() {
|
||||
self.ignore.insert(o);
|
||||
|
||||
// check if the inputs are constants, ie reduce to the form `coeff * ~one`
|
||||
let inputs = d
|
||||
.inputs
|
||||
.iter()
|
||||
// we need to reduce to the canonical form to interpret `a + 1 - a` as `1`
|
||||
.map(|i| LinComb::from(i.as_canonical()))
|
||||
.map(|l| match l.0.len() {
|
||||
// 0 is constant and can be represented by an empty lincomb
|
||||
0 => Ok(T::from(0)),
|
||||
_ => l
|
||||
// try to match to a single summand `coeff * v`
|
||||
.try_summand()
|
||||
.map(|(variable, coefficient)| match variable {
|
||||
// v must be ~one
|
||||
v if v == FlatVariable::one() => Ok(coefficient),
|
||||
_ => Err(LinComb::summand(coefficient, variable).into()),
|
||||
})
|
||||
.unwrap_or(Err(l.into())),
|
||||
})
|
||||
.collect::<Vec<Result<T, LinComb<T>>>>();
|
||||
|
||||
match inputs.iter().all(|r| r.is_ok()) {
|
||||
true => {
|
||||
// unwrap inputs to their constant value
|
||||
let inputs = inputs.into_iter().map(|i| i.unwrap()).collect();
|
||||
// run the interpereter
|
||||
let outputs = Interpreter::default()
|
||||
.execute_solver(&d.solver, &inputs)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(outputs.len(), d.outputs.len());
|
||||
|
||||
// insert the results in the substitution
|
||||
for (output, value) in d.outputs.into_iter().zip(outputs.into_iter()) {
|
||||
self.substitution.insert(output, value.into());
|
||||
}
|
||||
vec![]
|
||||
}
|
||||
false => {
|
||||
// reconstruct the input expressions
|
||||
let inputs = inputs
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
i.map(|v| LinComb::summand(v, FlatVariable::one()))
|
||||
.unwrap_or_else(|q| q)
|
||||
})
|
||||
.collect();
|
||||
// to prevent the optimiser from replacing variables introduced by directives, add them to the ignored set
|
||||
for o in d.outputs.iter().cloned() {
|
||||
self.ignore.insert(o);
|
||||
}
|
||||
vec![Statement::Directive(Directive { inputs, ..d })]
|
||||
}
|
||||
}
|
||||
vec![Statement::Directive(d)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_linear_combination(&mut self, lc: LinComb<T>) -> LinComb<T> {
|
||||
// for each summand, check if it is equal to a linear term in our substitution, otherwise keep it as is
|
||||
lc.0.into_iter()
|
||||
.map(|(variable, coefficient)| {
|
||||
self.substitution
|
||||
.get(&variable)
|
||||
.map(|l| l.clone() * &coefficient)
|
||||
.unwrap_or(LinComb::summand(coefficient, variable))
|
||||
})
|
||||
.fold(LinComb::zero(), |acc, x| acc + x)
|
||||
match lc
|
||||
.0
|
||||
.iter()
|
||||
.find(|(variable, _)| self.substitution.get(&variable).is_some())
|
||||
.is_some()
|
||||
{
|
||||
true =>
|
||||
// for each summand, check if it is equal to a linear term in our substitution, otherwise keep it as is
|
||||
{
|
||||
lc.0.into_iter()
|
||||
.map(|(variable, coefficient)| {
|
||||
self.substitution
|
||||
.get(&variable)
|
||||
.map(|l| l.clone() * &coefficient)
|
||||
.unwrap_or(LinComb::summand(coefficient, variable))
|
||||
})
|
||||
.fold(LinComb::zero(), |acc, x| acc + x)
|
||||
}
|
||||
false => lc,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_argument(&mut self, a: FlatVariable) -> FlatVariable {
|
||||
|
|
|
@ -5,7 +5,7 @@ extern crate zokrates_field;
|
|||
use std::io;
|
||||
use zokrates_common::Resolver;
|
||||
use zokrates_core::{
|
||||
compile::{compile, CompilationArtifacts},
|
||||
compile::{compile, CompilationArtifacts, CompileConfig},
|
||||
ir::Interpreter,
|
||||
};
|
||||
use zokrates_field::Bn128Field;
|
||||
|
@ -28,6 +28,7 @@ fn out_of_range() {
|
|||
source,
|
||||
"./path/to/file".into(),
|
||||
None::<&dyn Resolver<io::Error>>,
|
||||
&CompileConfig::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ use std::path::PathBuf;
|
|||
use wasm_bindgen::prelude::*;
|
||||
use zokrates_abi::{parse_strict, Decode, Encode, Inputs};
|
||||
use zokrates_common::Resolver;
|
||||
use zokrates_core::compile::{compile as core_compile, CompilationArtifacts, CompileError};
|
||||
use zokrates_core::compile::{
|
||||
compile as core_compile, CompilationArtifacts, CompileConfig, CompileError,
|
||||
};
|
||||
use zokrates_core::imports::Error;
|
||||
use zokrates_core::ir;
|
||||
use zokrates_core::proof_system::{self, ProofSystem, SolidityAbi};
|
||||
|
@ -104,6 +106,7 @@ pub fn compile(
|
|||
source.as_string().unwrap(),
|
||||
PathBuf::from(location.as_string().unwrap()),
|
||||
Some(&resolver),
|
||||
&CompileConfig::default().with_is_release(true),
|
||||
)
|
||||
.map_err(|ce| {
|
||||
JsValue::from_str(&format!(
|
||||
|
|
|
@ -75,7 +75,7 @@ fn compare<T: Field>(result: ir::ExecutionResult<T>, expected: TestResult) -> Re
|
|||
}
|
||||
|
||||
use std::io::{BufReader, Read};
|
||||
use zokrates_core::compile::compile;
|
||||
use zokrates_core::compile::{compile, CompileConfig};
|
||||
use zokrates_fs_resolver::FileSystemResolver;
|
||||
|
||||
pub fn test_inner(test_path: &str) {
|
||||
|
@ -97,7 +97,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, t.entry_point.clone(), Some(&resolver)).unwrap();
|
||||
|
||||
let artifacts = compile::<T, _>(
|
||||
code,
|
||||
t.entry_point.clone(),
|
||||
Some(&resolver),
|
||||
&CompileConfig::default().with_is_release(true),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bin = artifacts.prog();
|
||||
|
||||
|
|
Loading…
Reference in a new issue