clean, add boolean check to all boolean user input
This commit is contained in:
parent
a0c2663471
commit
79fea57be8
10 changed files with 211 additions and 51 deletions
16
t.code
16
t.code
|
@ -1,16 +0,0 @@
|
|||
from "./u.code" import Fooo
|
||||
|
||||
struct Bar {
|
||||
a: field,
|
||||
a: field,
|
||||
c: field,
|
||||
}
|
||||
|
||||
struct Baz {
|
||||
a: Bar
|
||||
}
|
||||
|
||||
def main(Bar a, Bar b, bool c) -> (Bar):
|
||||
Bar bar = Bar { a: 1, b: 1, c: 1 }
|
||||
return if false then a else bar fi
|
||||
|
4
u.code
4
u.code
|
@ -1,4 +0,0 @@
|
|||
struct Foo {
|
||||
a: field,
|
||||
b: field[2],
|
||||
}
|
|
@ -138,8 +138,6 @@ pub fn compile<T: Field, R: BufRead, S: BufRead, E: Into<imports::Error>>(
|
|||
|
||||
let source = arena.alloc(source);
|
||||
|
||||
println!("{:?}", source);
|
||||
|
||||
let compiled = compile_program(source, location.clone(), resolve_option, &arena)?;
|
||||
|
||||
// check semantics
|
||||
|
|
|
@ -1548,7 +1548,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
let arguments_flattened = funct
|
||||
.arguments
|
||||
.into_iter()
|
||||
.flat_map(|p| self.use_parameter(&p, &mut statements_flattened))
|
||||
.flat_map(|p| self.use_parameter(&p))
|
||||
.collect();
|
||||
|
||||
// flatten statements in functions and apply substitution
|
||||
|
@ -1602,19 +1602,8 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
vars
|
||||
}
|
||||
|
||||
fn use_parameter(
|
||||
&mut self,
|
||||
parameter: &Parameter<'ast>,
|
||||
statements: &mut Vec<FlatStatement<T>>,
|
||||
) -> Vec<FlatParameter> {
|
||||
fn use_parameter(&mut self, parameter: &Parameter<'ast>) -> Vec<FlatParameter> {
|
||||
let variables = self.use_variable(¶meter.id);
|
||||
match parameter.id.get_type() {
|
||||
Type::Boolean => statements.extend(Self::boolean_constraint(&variables)),
|
||||
Type::Array(box Type::Boolean, _) => {
|
||||
statements.extend(Self::boolean_constraint(&variables))
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
variables
|
||||
.into_iter()
|
||||
|
@ -1635,21 +1624,6 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
(0..count).map(|_| self.issue_new_variable()).collect()
|
||||
}
|
||||
|
||||
fn boolean_constraint(variables: &Vec<FlatVariable>) -> Vec<FlatStatement<T>> {
|
||||
variables
|
||||
.iter()
|
||||
.map(|v| {
|
||||
FlatStatement::Condition(
|
||||
FlatExpression::Identifier(*v),
|
||||
FlatExpression::Mult(
|
||||
box FlatExpression::Identifier(*v),
|
||||
box FlatExpression::Identifier(*v),
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// create an internal variable. We do not register it in the layout
|
||||
fn use_sym(&mut self) -> FlatVariable {
|
||||
self.issue_new_variable()
|
||||
|
|
120
zokrates_core/src/static_analysis/constrain_inputs.rs
Normal file
120
zokrates_core/src/static_analysis/constrain_inputs.rs
Normal file
|
@ -0,0 +1,120 @@
|
|||
//! Add runtime boolean checks on user inputs
|
||||
//! Example:
|
||||
//! ```
|
||||
//! struct Foo {
|
||||
//! bar: bool
|
||||
//! }
|
||||
//!
|
||||
//! def main(Foo f) -> ():
|
||||
//! f.bar == f.bar && f.bar
|
||||
//! return
|
||||
//! ```
|
||||
//! @file unroll.rs
|
||||
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
|
||||
//! @date 2018
|
||||
|
||||
use crate::typed_absy::folder::Folder;
|
||||
use crate::typed_absy::types::Type;
|
||||
use crate::typed_absy::*;
|
||||
use zokrates_field::field::Field;
|
||||
|
||||
pub struct InputConstrainer<'ast, T: Field> {
|
||||
constraints: Vec<TypedStatement<'ast, T>>,
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> InputConstrainer<'ast, T> {
|
||||
fn new() -> Self {
|
||||
InputConstrainer {
|
||||
constraints: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constrain(p: TypedProgram<T>) -> TypedProgram<T> {
|
||||
InputConstrainer::new().fold_program(p)
|
||||
}
|
||||
|
||||
fn constrain_expression(&mut self, e: TypedExpression<'ast, T>) {
|
||||
match e {
|
||||
TypedExpression::FieldElement(_) => {}
|
||||
TypedExpression::Boolean(b) => self.constraints.push(TypedStatement::Condition(
|
||||
b.clone().into(),
|
||||
BooleanExpression::And(box b.clone(), box b).into(),
|
||||
)),
|
||||
TypedExpression::Array(a) => {
|
||||
for i in 0..a.size() {
|
||||
let e = match a.inner_type() {
|
||||
Type::FieldElement => FieldElementExpression::select(
|
||||
a.clone(),
|
||||
FieldElementExpression::Number(T::from(i)),
|
||||
)
|
||||
.into(),
|
||||
Type::Boolean => BooleanExpression::select(
|
||||
a.clone(),
|
||||
FieldElementExpression::Number(T::from(i)),
|
||||
)
|
||||
.into(),
|
||||
Type::Array(..) => ArrayExpression::select(
|
||||
a.clone(),
|
||||
FieldElementExpression::Number(T::from(i)),
|
||||
)
|
||||
.into(),
|
||||
Type::Struct(..) => StructExpression::select(
|
||||
a.clone(),
|
||||
FieldElementExpression::Number(T::from(i)),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
|
||||
self.constrain_expression(e);
|
||||
}
|
||||
}
|
||||
TypedExpression::Struct(s) => {
|
||||
for (id, ty) in s.ty() {
|
||||
let e = match ty {
|
||||
Type::FieldElement => {
|
||||
FieldElementExpression::member(s.clone(), id.clone()).into()
|
||||
}
|
||||
Type::Boolean => BooleanExpression::member(s.clone(), id.clone()).into(),
|
||||
Type::Array(..) => ArrayExpression::member(s.clone(), id.clone()).into(),
|
||||
Type::Struct(..) => StructExpression::member(s.clone(), id.clone()).into(),
|
||||
};
|
||||
|
||||
self.constrain_expression(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast, T: Field> Folder<'ast, T> for InputConstrainer<'ast, T> {
|
||||
fn fold_parameter(&mut self, p: Parameter<'ast>) -> Parameter<'ast> {
|
||||
let v = p.id.clone();
|
||||
|
||||
let e = match v.get_type() {
|
||||
Type::FieldElement => FieldElementExpression::Identifier(v.id).into(),
|
||||
Type::Boolean => BooleanExpression::Identifier(v.id).into(),
|
||||
Type::Struct(members) => StructExpressionInner::Identifier(v.id)
|
||||
.annotate(members)
|
||||
.into(),
|
||||
Type::Array(box ty, size) => ArrayExpressionInner::Identifier(v.id)
|
||||
.annotate(ty, size)
|
||||
.into(),
|
||||
};
|
||||
|
||||
self.constrain_expression(e);
|
||||
|
||||
p
|
||||
}
|
||||
|
||||
fn fold_function(&mut self, f: TypedFunction<'ast, T>) -> TypedFunction<'ast, T> {
|
||||
TypedFunction {
|
||||
arguments: f
|
||||
.arguments
|
||||
.into_iter()
|
||||
.map(|a| self.fold_parameter(a))
|
||||
.collect(),
|
||||
statements: self.constraints.drain(..).chain(f.statements).collect(),
|
||||
..f
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,11 +4,13 @@
|
|||
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
|
||||
//! @date 2018
|
||||
|
||||
mod constrain_inputs;
|
||||
mod flat_propagation;
|
||||
mod inline;
|
||||
mod propagation;
|
||||
mod unroll;
|
||||
|
||||
use self::constrain_inputs::InputConstrainer;
|
||||
use self::inline::Inliner;
|
||||
use self::propagation::Propagator;
|
||||
use self::unroll::Unroller;
|
||||
|
@ -24,11 +26,12 @@ impl<'ast, T: Field> Analyse for TypedProgram<'ast, T> {
|
|||
fn analyse(self) -> Self {
|
||||
// unroll
|
||||
let r = Unroller::unroll(self);
|
||||
println!("{}", r);
|
||||
// inline
|
||||
let r = Inliner::inline(r);
|
||||
// propagate
|
||||
let r = Propagator::propagate(r);
|
||||
// constrain inputs
|
||||
let r = InputConstrainer::constrain(r);
|
||||
r
|
||||
}
|
||||
}
|
||||
|
|
2
zokrates_core_test/tests/tests/arrays/identity.code
Normal file
2
zokrates_core_test/tests/tests/arrays/identity.code
Normal file
|
@ -0,0 +1,2 @@
|
|||
def main(bool[3] a) -> (bool[3]):
|
||||
return a
|
38
zokrates_core_test/tests/tests/arrays/identity.json
Normal file
38
zokrates_core_test/tests/tests/arrays/identity.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/arrays/identity.code",
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["0", "0", "0"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["0", "0", "0"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["1", "0", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1", "0", "1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["2", "1", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "4",
|
||||
"right": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7
zokrates_core_test/tests/tests/structs/identity.code
Normal file
7
zokrates_core_test/tests/tests/structs/identity.code
Normal file
|
@ -0,0 +1,7 @@
|
|||
struct A {
|
||||
a: field,
|
||||
b: bool
|
||||
}
|
||||
|
||||
def main(A a) -> (A):
|
||||
return a
|
38
zokrates_core_test/tests/tests/structs/identity.json
Normal file
38
zokrates_core_test/tests/tests/structs/identity.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/structs/identity.code",
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "0"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["42", "0"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["42", "1"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "3"]
|
||||
},
|
||||
"output": {
|
||||
"Err": {
|
||||
"UnsatisfiedConstraint": {
|
||||
"left": "9",
|
||||
"right": "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue