Merge branch 'develop' of github.com:JacobEberhardt/ZoKrates into if-else-array
This commit is contained in:
commit
38be2a4890
9 changed files with 274 additions and 35 deletions
|
@ -380,21 +380,11 @@ fn main() {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let witness_map = program_ast
|
let witness = program_ast
|
||||||
.execute(arguments)
|
.execute(&arguments)
|
||||||
.unwrap_or_else(|e| panic!(format!("Execution failed: {}", e)));
|
.unwrap_or_else(|e| panic!(format!("Execution failed: {}", e)));
|
||||||
|
|
||||||
println!(
|
println!("\nWitness: \n\n{}", witness.format_outputs());
|
||||||
"\nWitness: \n\n{}",
|
|
||||||
witness_map
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(variable, value)| match variable {
|
|
||||||
variable if variable.is_output() => Some(format!("{} {}", variable, value)),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
);
|
|
||||||
|
|
||||||
// write witness to file
|
// write witness to file
|
||||||
let output_path = Path::new(sub_matches.value_of("output").unwrap());
|
let output_path = Path::new(sub_matches.value_of("output").unwrap());
|
||||||
|
@ -403,10 +393,7 @@ fn main() {
|
||||||
Err(why) => panic!("couldn't create {}: {}", output_path.display(), why),
|
Err(why) => panic!("couldn't create {}: {}", output_path.display(), why),
|
||||||
};
|
};
|
||||||
let mut bw = BufWriter::new(output_file);
|
let mut bw = BufWriter::new(output_file);
|
||||||
for (var, val) in &witness_map {
|
write!(&mut bw, "{}", witness).expect("Unable to write data to file.");
|
||||||
write!(&mut bw, "{} {}\n", var, val.to_dec_string())
|
|
||||||
.expect("Unable to write data to file.");
|
|
||||||
}
|
|
||||||
bw.flush().expect("Unable to flush buffer.");
|
bw.flush().expect("Unable to flush buffer.");
|
||||||
}
|
}
|
||||||
#[cfg(feature = "libsnark")]
|
#[cfg(feature = "libsnark")]
|
||||||
|
@ -656,7 +643,7 @@ mod tests {
|
||||||
|
|
||||||
let (..) = r1cs_program(program_flattened.clone());
|
let (..) = r1cs_program(program_flattened.clone());
|
||||||
let _ = program_flattened
|
let _ = program_flattened
|
||||||
.execute(vec![FieldPrime::from(0)])
|
.execute(&vec![FieldPrime::from(0)])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -690,7 +677,7 @@ mod tests {
|
||||||
|
|
||||||
let result = std::panic::catch_unwind(|| {
|
let result = std::panic::catch_unwind(|| {
|
||||||
let _ = program_flattened
|
let _ = program_flattened
|
||||||
.execute(vec![FieldPrime::from(0)])
|
.execute(&vec![FieldPrime::from(0)])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
|
|
|
@ -3,17 +3,57 @@ use ir::*;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use zokrates_field::field::Field;
|
use zokrates_field::field::Field;
|
||||||
|
|
||||||
|
pub type ExecutionResult<T> = Result<Witness<T>, Error>;
|
||||||
|
|
||||||
|
pub struct Witness<T: Field>(BTreeMap<FlatVariable, T>);
|
||||||
|
|
||||||
|
impl<T: Field> Witness<T> {
|
||||||
|
pub fn return_values(&self) -> Vec<T> {
|
||||||
|
self.0
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(k, _)| k.is_output())
|
||||||
|
.map(|(_, v)| v)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_outputs(&self) -> String {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(variable, value)| match variable {
|
||||||
|
variable if variable.is_output() => Some(format!("{} {}", variable, value)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Field> fmt::Display for Witness<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{} {}", k, v.to_dec_string()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Field> Prog<T> {
|
impl<T: Field> Prog<T> {
|
||||||
pub fn execute(self, inputs: Vec<T>) -> Result<BTreeMap<FlatVariable, T>, Error<T>> {
|
pub fn execute<U: Into<T> + Clone>(&self, inputs: &Vec<U>) -> ExecutionResult<T> {
|
||||||
let main = self.main;
|
let main = &self.main;
|
||||||
assert_eq!(main.arguments.len(), inputs.len());
|
self.check_inputs(&inputs)?;
|
||||||
let mut witness = BTreeMap::new();
|
let mut witness = BTreeMap::new();
|
||||||
witness.insert(FlatVariable::one(), T::one());
|
witness.insert(FlatVariable::one(), T::one());
|
||||||
for (arg, value) in main.arguments.iter().zip(inputs.iter()) {
|
for (arg, value) in main.arguments.iter().zip(inputs.iter()) {
|
||||||
witness.insert(arg.clone(), value.clone());
|
witness.insert(arg.clone(), value.clone().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
for statement in main.statements {
|
for statement in &main.statements {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::Constraint(quad, lin) => match lin.is_assignee(&witness) {
|
Statement::Constraint(quad, lin) => match lin.is_assignee(&witness) {
|
||||||
true => {
|
true => {
|
||||||
|
@ -24,7 +64,10 @@ impl<T: Field> Prog<T> {
|
||||||
let lhs_value = quad.evaluate(&witness);
|
let lhs_value = quad.evaluate(&witness);
|
||||||
let rhs_value = lin.evaluate(&witness);
|
let rhs_value = lin.evaluate(&witness);
|
||||||
if lhs_value != rhs_value {
|
if lhs_value != rhs_value {
|
||||||
return Err(Error::Constraint(quad, lin, lhs_value, rhs_value));
|
return Err(Error::UnsatisfiedConstraint {
|
||||||
|
left: lhs_value.to_dec_string(),
|
||||||
|
right: rhs_value.to_dec_string(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -44,7 +87,18 @@ impl<T: Field> Prog<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(witness)
|
Ok(Witness(witness))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_inputs<U>(&self, inputs: &Vec<U>) -> Result<(), Error> {
|
||||||
|
if self.main.arguments.len() == inputs.len() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::WrongInputCount {
|
||||||
|
expected: self.main.arguments.len(),
|
||||||
|
received: inputs.len(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,21 +123,35 @@ impl<T: Field> QuadComb<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Error<T: Field> {
|
pub enum Error {
|
||||||
Constraint(QuadComb<T>, LinComb<T>, T, T),
|
UnsatisfiedConstraint { left: String, right: String },
|
||||||
Solver,
|
Solver,
|
||||||
|
WrongInputCount { expected: usize, received: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Field> fmt::Display for Error<T> {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Constraint(ref quad, ref lin, ref left_value, ref right_value) => write!(
|
Error::UnsatisfiedConstraint {
|
||||||
f,
|
ref left,
|
||||||
"Expected {} to equal {}, but {} != {}",
|
ref right,
|
||||||
quad, lin, left_value, right_value
|
} => write!(f, "Expected {} to equal {}", left, right),
|
||||||
),
|
|
||||||
Error::Solver => write!(f, ""),
|
Error::Solver => write!(f, ""),
|
||||||
|
Error::WrongInputCount { expected, received } => write!(
|
||||||
|
f,
|
||||||
|
"Program takes {} input{} but was passed {} value{}",
|
||||||
|
expected,
|
||||||
|
if expected == 1 { "" } else { "s" },
|
||||||
|
received,
|
||||||
|
if received == 1 { "" } else { "s" }
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ mod interpreter;
|
||||||
use self::expression::LinComb;
|
use self::expression::LinComb;
|
||||||
use self::expression::QuadComb;
|
use self::expression::QuadComb;
|
||||||
|
|
||||||
|
pub use self::interpreter::Error;
|
||||||
|
pub use self::interpreter::ExecutionResult;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub enum Statement<T: Field> {
|
pub enum Statement<T: Field> {
|
||||||
Constraint(QuadComb<T>, LinComb<T>),
|
Constraint(QuadComb<T>, LinComb<T>),
|
||||||
|
|
2
zokrates_core/tests/bench/add.code
Normal file
2
zokrates_core/tests/bench/add.code
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def main(field a, field b) -> (field):
|
||||||
|
return a + b
|
27
zokrates_core/tests/bench/add.json
Normal file
27
zokrates_core/tests/bench/add.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"values": ["1", "2"]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"Ok": {
|
||||||
|
"values": ["3"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"values": ["1", "2", "42"]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"Err": {
|
||||||
|
"WrongInputCount": {
|
||||||
|
"expected": 2,
|
||||||
|
"received": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
zokrates_core/tests/bench/assert_one.code
Normal file
3
zokrates_core/tests/bench/assert_one.code
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
def main(field a) -> (field):
|
||||||
|
a == 1
|
||||||
|
return 1
|
17
zokrates_core/tests/bench/assert_one.json
Normal file
17
zokrates_core/tests/bench/assert_one.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"values": ["0"]
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"Err": {
|
||||||
|
"UnsatisfiedConstraint": {
|
||||||
|
"left": "1",
|
||||||
|
"right": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
12
zokrates_core/tests/integration.rs
Normal file
12
zokrates_core/tests/integration.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate zokrates_core;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
zokrates_test! {
|
||||||
|
add,
|
||||||
|
assert_one,
|
||||||
|
}
|
120
zokrates_core/tests/utils/mod.rs
Normal file
120
zokrates_core/tests/utils/mod.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use zokrates_core::compile::{compile as generic_compile, CompileError};
|
||||||
|
use zokrates_core::field::{Field, FieldPrime};
|
||||||
|
use zokrates_core::ir;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Tests {
|
||||||
|
pub tests: Vec<Test>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Input {
|
||||||
|
pub values: Vec<Val>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Test {
|
||||||
|
pub input: Input,
|
||||||
|
pub output: TestResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TestResult = Result<Output, ir::Error>;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct ComparableResult(Result<Vec<FieldPrime>, ir::Error>);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Output {
|
||||||
|
values: Vec<Val>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Val = String;
|
||||||
|
|
||||||
|
impl From<ir::ExecutionResult<FieldPrime>> for ComparableResult {
|
||||||
|
fn from(r: ir::ExecutionResult<FieldPrime>) -> ComparableResult {
|
||||||
|
ComparableResult(r.map(|v| v.return_values()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TestResult> for ComparableResult {
|
||||||
|
fn from(r: TestResult) -> ComparableResult {
|
||||||
|
ComparableResult(r.map(|v| {
|
||||||
|
v.values
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| FieldPrime::from_dec_string(v))
|
||||||
|
.collect()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(
|
||||||
|
result: ir::ExecutionResult<FieldPrime>,
|
||||||
|
expected: TestResult,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
// extract outputs from result
|
||||||
|
let result = ComparableResult::from(result);
|
||||||
|
// deserialize expected result
|
||||||
|
let expected = ComparableResult::from(expected);
|
||||||
|
|
||||||
|
if result != expected {
|
||||||
|
return Err(format!(
|
||||||
|
"Expected {:?} but found {:?}",
|
||||||
|
expected.0, result.0
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_file(path: &str) -> String {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
let mut file = File::open(format!("./tests/bench/{}", path)).expect("Unable to open the file");
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents)
|
||||||
|
.expect("Unable to read the file");
|
||||||
|
|
||||||
|
contents
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(code: &str) -> Result<ir::Prog<FieldPrime>, CompileError<FieldPrime>> {
|
||||||
|
generic_compile::<FieldPrime, &[u8], &[u8], io::Error>(&mut code.as_bytes(), None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! zokrates_test {
|
||||||
|
($($name:ident,)*) => {
|
||||||
|
$(
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
|
||||||
|
use zokrates_core::field::{FieldPrime, Field};
|
||||||
|
|
||||||
|
let code_string = $crate::utils::read_file(&format!("./{}.code", stringify!($name)));
|
||||||
|
let test_string = $crate::utils::read_file(&format!("./{}.json", stringify!($name)));
|
||||||
|
|
||||||
|
let bin = $crate::utils::compile(&code_string).unwrap();
|
||||||
|
|
||||||
|
let t: $crate::utils::Tests = serde_json::from_str(&test_string).unwrap();
|
||||||
|
|
||||||
|
for test in t.tests.into_iter() {
|
||||||
|
let input = &test.input.values;
|
||||||
|
let output = bin.execute(&input.iter().map(|v| FieldPrime::from_dec_string(v.clone())).collect());
|
||||||
|
|
||||||
|
let context = format!("
|
||||||
|
{}
|
||||||
|
|
||||||
|
Called with input ({})
|
||||||
|
", code_string, input.iter().map(|i| format!("{}", i)).collect::<Vec<_>>().join(", "));
|
||||||
|
|
||||||
|
match $crate::utils::compare(output, test.output) {
|
||||||
|
Err(e) => panic!("{}{}", context, e),
|
||||||
|
Ok(..) => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue