1
0
Fork 0
mirror of synced 2025-09-23 20:28:36 +00:00

Merge pull request #15 from Schaeff/explicit-private-inputs

Make private inputs explicit
This commit is contained in:
JacobEberhardt 2018-01-22 14:57:14 +01:00 committed by GitHub
commit 706e22727e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 451 additions and 34 deletions

View file

@ -1,4 +1,4 @@
// a and b are factorization of c
def main(c):
def main(c, private a, private b):
c == a * b
return 1

View file

@ -22,7 +22,7 @@ def validateInput(x):
return (x-1)*(x-2)*(x-3)*(x-4)
// variables naming: box'row''column'
def main(a21,b11,b22,c11,c22,d21):
def main(a21, b11, b22, c11, c22, d21, private a11, private a12, private a22, private b12, private b21, private c12, private c21, private d11, private d12, private d22):
// validate inputs
0 == validateInput(a11)

View file

@ -7,10 +7,9 @@
use std::fmt;
use std::collections::HashMap;
use std::io::{stdin, BufRead};
use field::Field;
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct Prog<T: Field> {
/// Functions of the program
pub functions: Vec<Function<T>>,
@ -56,7 +55,7 @@ impl<T: Field> fmt::Debug for Prog<T> {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct Function<T: Field> {
/// Name of the program
pub id: String,
@ -195,11 +194,13 @@ impl<T: Field> fmt::Debug for Statement<T> {
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct Parameter {
pub id: String,
pub private: bool,
}
impl fmt::Display for Parameter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.id)
let visibility = if self.private { "private " } else { "" };
write!(f, "{}{}", visibility, self.id)
}
}
@ -288,19 +289,11 @@ impl<T: Field> Expression<T> {
}
assert_eq!(num, T::zero());
} else {
println!(
"Could not calculate variable {:?}, inputs: {:?}",
panic!(
"Variable {:?} is undeclared in inputs: {:?}",
var,
inputs
);
println!("Please enter a value for {:?}:", var);
let mut input = String::new();
let stdin = stdin();
stdin
.lock()
.read_line(&mut input)
.expect("Did not enter a correct String");
inputs.insert(var.to_string(), T::from(input.trim()));
}
}
inputs[var].clone()

View file

@ -617,6 +617,7 @@ impl Flattener {
for arg in funct.arguments {
arguments_flattened.push(Parameter {
id: arg.id.to_string(),
private: arg.private
});
}
// flatten statements in functions and apply substitution

View file

@ -18,6 +18,7 @@ extern crate regex;
mod absy;
mod parser;
mod semantics;
mod flatten;
mod r1cs;
mod field;
@ -26,13 +27,14 @@ mod libsnark;
use std::fs::File;
use std::path::Path;
use std::io::{BufWriter, Write, BufReader, BufRead};
use std::io::{BufWriter, Write, BufReader, BufRead, stdin};
use std::collections::HashMap;
use std::string::String;
use std::io::prelude::*;
use field::{Field, FieldPrime};
use absy::Prog;
use parser::parse_program;
use semantics::Checker;
use flatten::Flattener;
use r1cs::r1cs_program;
use clap::{App, AppSettings, Arg, SubCommand};
@ -139,7 +141,7 @@ fn main() {
.arg(Arg::with_name("input")
.short("i")
.long("input")
.help("path of comiled code.")
.help("path of compiled code.")
.value_name("FILE")
.takes_value(true)
.required(false)
@ -159,6 +161,10 @@ fn main() {
.takes_value(true)
.multiple(true) // allows multiple values
.required(false)
).arg(Arg::with_name("interactive")
.long("interactive")
.help("enter private inputs interactively.")
.required(false)
)
)
.subcommand(SubCommand::with_name("generate-proof")
@ -209,6 +215,12 @@ fn main() {
}
};
// check semantics
match Checker::new().check_program(program_ast.clone()) {
Ok(()) => (),
Err(why) => panic!("Semantic analysis failed with: {}", why)
};
// flatten input program
let program_flattened =
Flattener::new(FieldPrime::get_required_bits()).flatten_program(program_ast);
@ -286,22 +298,54 @@ fn main() {
println!("{}", main_flattened);
// validate #arguments
let mut args: Vec<FieldPrime> = Vec::new();
let mut cli_arguments: Vec<FieldPrime> = Vec::new();
match sub_matches.values_of("arguments"){
Some(p) => {
let arg_strings: Vec<&str> = p.collect();
args = arg_strings.into_iter().map(|x| FieldPrime::from(x)).collect();
cli_arguments = arg_strings.into_iter().map(|x| FieldPrime::from(x)).collect();
},
None => {
}
}
if main_flattened.arguments.len() != args.len() {
println!("Wrong number of arguments. Given: {}, Required: {}.", args.len(), main_flattened.arguments.len());
// handle interactive and non-interactive modes
let is_interactive = sub_matches.occurrences_of("interactive") > 0;
// in interactive mode, only public inputs are expected
let expected_cli_args_count = main_flattened.arguments.iter().filter(|x| !(x.private && is_interactive)).count();
if cli_arguments.len() != expected_cli_args_count {
println!("Wrong number of arguments. Given: {}, Required: {}.", cli_arguments.len(), expected_cli_args_count);
std::process::exit(1);
}
let witness_map = main_flattened.get_witness(args);
let mut cli_arguments_iter = cli_arguments.into_iter();
let arguments = main_flattened.arguments.clone().into_iter().map(|x| {
match x.private && is_interactive {
// private inputs are passed interactively when the flag is present
true => {
println!("Please enter a value for {:?}:", x.id);
let mut input = String::new();
let stdin = stdin();
stdin
.lock()
.read_line(&mut input)
.expect("Did not enter a correct String");
FieldPrime::from(input.trim())
}
// otherwise, they are taken from the CLI arguments
false => {
match cli_arguments_iter.next() {
Some(x) => x,
None => {
std::process::exit(1);
}
}
}
}
}).collect();
let witness_map = main_flattened.get_witness(arguments);
// let witness_map: HashMap<String, FieldPrime> = main_flattened.get_witness(args);
println!("Witness: {:?}", witness_map);
match witness_map.get("~out") {
@ -381,7 +425,7 @@ fn main() {
// run setup phase
#[cfg(not(feature="nolibsnark"))]{
// number of inputs in the zkSNARK sense, i.e., input variables + output variables
let num_inputs = main_flattened.arguments.len() + 1; //currently exactly one output variable
let num_inputs = main_flattened.arguments.iter().filter(|x| !x.private).count() + 1;
println!("setup successful: {:?}", setup(variables, a, b, c, num_inputs, pk_path, vk_path));
}
}

View file

@ -150,6 +150,7 @@ enum Token<T: Field> {
Mult,
Div,
Pow,
Private,
Ide(String),
Num(T),
Unknown(String),
@ -188,6 +189,7 @@ impl<T: Field> fmt::Display for Token<T> {
Token::Mult => write!(f, "*"),
Token::Div => write!(f, "/"),
Token::Pow => write!(f, "**"),
Token::Private => write!(f, "private"),
Token::Ide(ref x) => write!(f, "{}", x),
Token::Num(ref x) => write!(f, "{}", x),
Token::Unknown(ref x) => write!(f, "{}", x),
@ -502,6 +504,14 @@ fn next_token<T: Field>(input: &String, pos: &Position) -> (Token<T>, String, Po
},
)
}
Some(_) if input[offset..].starts_with("private ") => (
Token::Private,
input[offset + 8..].to_string(),
Position {
line: pos.line,
col: pos.col + offset + 8,
},
),
Some(x) => match x {
'0'...'9' => parse_num(
&input[offset..].to_string(),
@ -1132,8 +1142,55 @@ fn parse_function<T: Field>(
let mut p = p3;
loop {
match next_token(&s, &p) {
(Token::Private, s4, p4) => {
match next_token(&s4, &p4) {
(Token::Ide(x), s5, p5) => {
args.push(Parameter { id: x, private: true });
match next_token(&s5, &p5) {
(Token::Comma, s6, p6) => {
s = s6;
p = p6;
}
(Token::Close, s5, p5) => match next_token(&s5, &p5) {
(Token::Colon, s6, p6) => match next_token(&s6, &p6) {
(Token::InlineComment(_), _, _) => break,
(Token::Unknown(ref x6), ..) if x6 == "" => break,
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Unknown("".to_string())],
got: t6,
pos: p6,
})
}
},
(t6, _, p6) => {
return Err(Error {
expected: vec![Token::Colon],
got: t6,
pos: p6,
})
}
},
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t5,
pos: p5,
})
}
}
}
(t5, _, p5) => {
return Err(Error {
expected: vec![Token::Comma, Token::Close],
got: t5,
pos: p5,
})
}
}
}
(Token::Ide(x), s4, p4) => {
args.push(Parameter { id: x });
args.push(Parameter { id: x, private: false });
match next_token(&s4, &p4) {
(Token::Comma, s5, p5) => {
s = s5;

View file

@ -290,7 +290,8 @@ pub fn r1cs_program<T: Field>(
.iter()
.find(|x: &&Function<T>| x.id == "main".to_string())
.unwrap();
variables.extend(main.arguments.iter().map(|x| format!("{}", x)));
variables.extend(main.arguments.iter().filter(|x| !x.private).map(|x| format!("{}", x)));
// ~out is added after main's arguments as we want variables (columns)
// in the r1cs to be aligned like "public inputs | private inputs"
variables.push("~out".to_string());
@ -311,14 +312,7 @@ pub fn r1cs_program<T: Field>(
&mut b_row,
&mut c_row,
),
Statement::Definition(ref id, ref expr) => r1cs_expression(
Identifier(id.to_string()),
expr.clone(),
&mut variables,
&mut a_row,
&mut b_row,
&mut c_row,
),
Statement::Definition(_, _) => continue,
Statement::Condition(ref expr1, ref expr2) => r1cs_expression(
expr1.clone(),
expr2.clone(),

328
src/semantics.rs Normal file
View file

@ -0,0 +1,328 @@
//! Module containing semantic analysis tools to run at compile time
//! The goal is to detect semantic errors such as undefined variables
//! A variable is undefined if it isn't present in the static scope
//!
//! @file semantics.rs
//! @author Thibaut Schaeffer <thibaut@schaeff.fr>
//! @date 2017
use std::collections::HashSet;
use absy::*;
use field::Field;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Symbol {
id: String,
level: usize
}
// Checker, checks the semantics of a program.
pub struct Checker {
scope: HashSet<Symbol>,
level: usize
}
impl Checker {
pub fn new() -> Checker {
Checker {
scope: HashSet::new(),
level: 0
}
}
#[test]
pub fn new_with_args(scope: HashSet<Symbol>, level: usize) -> Checker {
Checker {
scope: scope,
level: level
}
}
pub fn check_program<T: Field>(&mut self, prog: Prog<T>) -> Result<(), String> {
for func in prog.functions {
self.check_function(func)?;
}
Ok(())
}
fn check_function<T: Field>(&mut self, funct: Function<T>) -> Result<(), String> {
self.level += 1;
for arg in funct.arguments {
self.scope.insert(Symbol {
id: arg.id.to_string(),
level: self.level
});
}
for stat in funct.statements {
self.check_statement(stat)?;
}
let current_level = self.level.clone();
let current_scope = self.scope.clone();
let to_remove = current_scope.iter().filter(|symbol| symbol.level == current_level);
for symbol in to_remove {
self.scope.remove(symbol);
}
self.level -= 1;
Ok(())
}
fn check_statement<T: Field>(&mut self, stat: Statement<T>) -> Result<(), String> {
match stat {
Statement::Return(expr) => {
self.check_expression(expr)?;
Ok(())
}
Statement::Definition(id, expr) | Statement::Compiler(id, expr) => {
self.check_expression(expr)?;
self.scope.insert(Symbol {
id: id.to_string(),
level: self.level
});
Ok(())
}
Statement::Condition(lhs, rhs) => {
self.check_expression(lhs)?;
self.check_expression(rhs)?;
Ok(())
}
Statement::For(id, _, _, statements) => {
self.level += 1;
let index = Symbol {
id: id.to_string(),
level: self.level
};
self.scope.insert(index.clone());
for stat in statements {
self.check_statement(stat)?;
}
self.scope.remove(&index);
self.level -= 1;
Ok(())
}
}
}
fn check_expression<T: Field>(&mut self, expr: Expression<T>) -> Result<(), String> {
match expr {
Expression::Identifier(id) => {
// check that `id` is defined in the scope
match self.scope.iter().filter(|symbol| symbol.id == id.to_string()).count() {
0 => Err(format!("{:?} is undefined", id.to_string())),
_ => Ok(()),
}
}
Expression::Add(box e1, box e2) | Expression::Sub(box e1, box e2) | Expression::Mult(box e1, box e2) |
Expression::Div(box e1, box e2) | Expression::Pow(box e1, box e2) => {
self.check_expression(e1)?;
self.check_expression(e2)?;
Ok(())
}
Expression::IfElse(box condition, box consequence, box alternative) => {
self.check_condition(condition)?;
self.check_expression(consequence)?;
self.check_expression(alternative)?;
Ok(())
}
Expression::FunctionCall(_, param_expressions) => {
for expr in param_expressions {
self.check_expression(expr)?;
}
Ok(())
}
Expression::Number(_) => Ok(())
}
}
fn check_condition<T: Field>(&mut self, cond: Condition<T>) -> Result<(), String> {
match cond {
Condition::Lt(e1, e2) |
Condition::Le(e1, e2) |
Condition::Eq(e1, e2) |
Condition::Ge(e1, e2) |
Condition::Gt(e1, e2) => {
self.check_expression(e1)?;
self.check_expression(e2)?;
Ok(())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use field::FieldPrime;
#[test]
fn undefined_variable_in_statement() {
// a = b
// b undefined
let statement: Statement<FieldPrime> = Statement::Definition(
String::from("a"),
Expression::Identifier(String::from("b"))
);
let mut checker = Checker::new();
assert_eq!(checker.check_statement(statement), Err("\"b\" is undefined".to_string()));
}
#[test]
fn defined_variable_in_statement() {
// a = b
// b defined
let statement: Statement<FieldPrime> = Statement::Definition(
String::from("a"),
Expression::Identifier(String::from("b"))
);
let mut scope = HashSet::new();
scope.insert(Symbol {
id: String::from("b"),
level: 0
});
let mut checker = Checker::new_with_args(scope, 1);
assert_eq!(checker.check_statement(statement), Ok(()));
}
#[test]
fn declared_in_other_function() {
// def foo():
// a = 1
// def bar():
// return a
// should fail
let foo_args = Vec::<Parameter>::new();
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
foo_statements.push(Statement::Definition(
String::from("a"),
Expression::Number(FieldPrime::from(1)))
);
let foo = Function {
id: "foo".to_string(),
arguments: foo_args,
statements: foo_statements,
};
let bar_args = Vec::<Parameter>::new();
let mut bar_statements = Vec::<Statement<FieldPrime>>::new();
bar_statements.push(Statement::Return(
Expression::Identifier(String::from("a"))
));
let bar = Function {
id: "bar".to_string(),
arguments: bar_args,
statements: bar_statements,
};
let mut funcs = Vec::<Function<FieldPrime>>::new();
funcs.push(foo);
funcs.push(bar);
let prog = Prog {
functions: funcs
};
let mut checker = Checker::new();
assert_eq!(checker.check_program(prog), Err("\"a\" is undefined".to_string()));
}
#[test]
fn declared_in_two_scopes() {
// def foo():
// a = 1
// def bar():
// a = 2
// return a
// should pass
let foo_args = Vec::<Parameter>::new();
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
foo_statements.push(Statement::Definition(
String::from("a"),
Expression::Number(FieldPrime::from(1)))
);
let foo = Function {
id: "foo".to_string(),
arguments: foo_args,
statements: foo_statements,
};
let bar_args = Vec::<Parameter>::new();
let mut bar_statements = Vec::<Statement<FieldPrime>>::new();
bar_statements.push(Statement::Definition(
String::from("a"),
Expression::Number(FieldPrime::from(2))
));
bar_statements.push(Statement::Return(
Expression::Identifier(String::from("a"))
));
let bar = Function {
id: "bar".to_string(),
arguments: bar_args,
statements: bar_statements,
};
let mut funcs = Vec::<Function<FieldPrime>>::new();
funcs.push(foo);
funcs.push(bar);
let prog = Prog {
functions: funcs
};
let mut checker = Checker::new();
assert_eq!(checker.check_program(prog), Ok(()));
}
#[test]
fn for_index_after_end() {
// def foo():
// for i in 0..10 do
// endfor
// return i
// should fail
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
foo_statements.push(Statement::For(
String::from("i"),
FieldPrime::from(0),
FieldPrime::from(10),
Vec::<Statement<FieldPrime>>::new())
);
foo_statements.push(Statement::Return(
Expression::Identifier(String::from("i"))
));
let foo = Function {
id: "foo".to_string(),
arguments: Vec::<Parameter>::new(),
statements: foo_statements
};
let mut checker = Checker::new();
assert_eq!(checker.check_function(foo), Err("\"i\" is undefined".to_string()));
}
#[test]
fn for_index_in_for() {
// def foo():
// for i in 0..10 do
// a = i
// endfor
// should pass
let mut foo_statements = Vec::<Statement<FieldPrime>>::new();
let mut for_statements = Vec::<Statement<FieldPrime>>::new();
for_statements.push(Statement::Definition(
String::from("a"),
Expression::Identifier(String::from("i"))
));
foo_statements.push(Statement::For(
String::from("i"),
FieldPrime::from(0),
FieldPrime::from(10),
for_statements
));
let foo = Function {
id: "foo".to_string(),
arguments: Vec::<Parameter>::new(),
statements: foo_statements
};
let mut checker = Checker::new();
assert_eq!(checker.check_function(foo), Ok(()));
}
}