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();
|
||||
|
||||
let witness_map = program_ast
|
||||
.execute(arguments)
|
||||
let witness = program_ast
|
||||
.execute(&arguments)
|
||||
.unwrap_or_else(|e| panic!(format!("Execution failed: {}", e)));
|
||||
|
||||
println!(
|
||||
"\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")
|
||||
);
|
||||
println!("\nWitness: \n\n{}", witness.format_outputs());
|
||||
|
||||
// write witness to file
|
||||
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),
|
||||
};
|
||||
let mut bw = BufWriter::new(output_file);
|
||||
for (var, val) in &witness_map {
|
||||
write!(&mut bw, "{} {}\n", var, val.to_dec_string())
|
||||
.expect("Unable to write data to file.");
|
||||
}
|
||||
write!(&mut bw, "{}", witness).expect("Unable to write data to file.");
|
||||
bw.flush().expect("Unable to flush buffer.");
|
||||
}
|
||||
#[cfg(feature = "libsnark")]
|
||||
|
@ -656,7 +643,7 @@ mod tests {
|
|||
|
||||
let (..) = r1cs_program(program_flattened.clone());
|
||||
let _ = program_flattened
|
||||
.execute(vec![FieldPrime::from(0)])
|
||||
.execute(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -690,7 +677,7 @@ mod tests {
|
|||
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
let _ = program_flattened
|
||||
.execute(vec![FieldPrime::from(0)])
|
||||
.execute(&vec![FieldPrime::from(0)])
|
||||
.unwrap();
|
||||
});
|
||||
assert!(result.is_err());
|
||||
|
|
|
@ -3,17 +3,57 @@ use ir::*;
|
|||
use std::collections::BTreeMap;
|
||||
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> {
|
||||
pub fn execute(self, inputs: Vec<T>) -> Result<BTreeMap<FlatVariable, T>, Error<T>> {
|
||||
let main = self.main;
|
||||
assert_eq!(main.arguments.len(), inputs.len());
|
||||
pub fn execute<U: Into<T> + Clone>(&self, inputs: &Vec<U>) -> ExecutionResult<T> {
|
||||
let main = &self.main;
|
||||
self.check_inputs(&inputs)?;
|
||||
let mut witness = BTreeMap::new();
|
||||
witness.insert(FlatVariable::one(), T::one());
|
||||
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 {
|
||||
Statement::Constraint(quad, lin) => match lin.is_assignee(&witness) {
|
||||
true => {
|
||||
|
@ -24,7 +64,10 @@ impl<T: Field> Prog<T> {
|
|||
let lhs_value = quad.evaluate(&witness);
|
||||
let rhs_value = lin.evaluate(&witness);
|
||||
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)]
|
||||
pub enum Error<T: Field> {
|
||||
Constraint(QuadComb<T>, LinComb<T>, T, T),
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub enum Error {
|
||||
UnsatisfiedConstraint { left: String, right: String },
|
||||
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 {
|
||||
match *self {
|
||||
Error::Constraint(ref quad, ref lin, ref left_value, ref right_value) => write!(
|
||||
f,
|
||||
"Expected {} to equal {}, but {} != {}",
|
||||
quad, lin, left_value, right_value
|
||||
),
|
||||
Error::UnsatisfiedConstraint {
|
||||
ref left,
|
||||
ref right,
|
||||
} => write!(f, "Expected {} to equal {}", left, right),
|
||||
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::QuadComb;
|
||||
|
||||
pub use self::interpreter::Error;
|
||||
pub use self::interpreter::ExecutionResult;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum Statement<T: Field> {
|
||||
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