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

merge dev

This commit is contained in:
dark64 2020-07-01 13:43:48 +02:00
commit fcec50f614
13 changed files with 185 additions and 36 deletions

View file

@ -1,2 +1,5 @@
struct Bar {
}
def main() -> (field):
return 21

View file

@ -1,2 +1,5 @@
struct Baz {
}
def main() -> (field):
return 123

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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