enforce name, type, visibility of inputs. add tests

This commit is contained in:
collin 2020-06-11 14:40:27 -07:00
parent b44e336cb6
commit 3f668422fd
19 changed files with 177 additions and 26 deletions

View File

@ -388,7 +388,7 @@ Public and private inputs for a Leo program are specified in the `inputs/` direc
## Sections
A Leo input file is made up of sections. Sections are defined by a section header in brackets followed by one or more input definitions.
Section headers specify the target file which must have a main function with matching input names and types defined in the same order.
Section headers specify the target file which must have a main function with matching input names and types.
`inputs/inputs.leo`

View File

@ -103,7 +103,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
let package_name = self.package_name.clone();
self.program = Program::from(syntax_tree, package_name);
self.program_inputs.set_inputs_size(self.program.num_parameters);
self.program_inputs.set_inputs_size(self.program.expected_inputs.len());
log::debug!("Program parsing complete\n{:#?}", self.program);
@ -114,7 +114,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> Compiler<F, G> {
let syntax_tree = LeoInputsParser::parse_file(input_file_path, input_file_string)?;
// Check number/order of private parameters here
self.program_inputs = Inputs::from_inputs_file(syntax_tree)?;
self.program_inputs = Inputs::from_inputs_file(syntax_tree, self.program.expected_inputs.clone())?;
Ok(())
}

View File

@ -0,0 +1,2 @@
[main]
b: private bool = true;

View File

@ -0,0 +1,2 @@
[main]
bad_name: private bool = true;

View File

@ -0,0 +1,2 @@
[main]
b: private u8 = 1;

View File

@ -0,0 +1,2 @@
[main]
b: public bool = true;

View File

@ -0,0 +1,3 @@
[main]
b: private bool = true;
a: public bool = false;

View File

@ -0,0 +1,3 @@
function main(b: private bool) -> bool {
return b
}

View File

@ -0,0 +1,3 @@
function main(a: public bool, b: private bool) -> bool {
return a || b
}

View File

@ -0,0 +1,72 @@
use crate::{boolean::output_true, parse_program};
use leo_compiler::errors::CompilerError;
use leo_inputs::InputParserError;
use std::path::PathBuf;
fn fail_input_parser(error: CompilerError) {
match error {
CompilerError::InputParserError(InputParserError::InputNotFound(_)) => {}
err => panic!("expected input parser error, got {}", err),
}
}
#[test]
fn test_inputs_pass() {
let program_bytes = include_bytes!("main.leo");
let input_bytes = include_bytes!("inputs.leo");
let input_string = String::from_utf8_lossy(input_bytes);
let mut program = parse_program(program_bytes).unwrap();
program.parse_inputs(&PathBuf::new(), &input_string).unwrap();
output_true(program);
}
#[test]
fn test_inputs_fail_name() {
let program_bytes = include_bytes!("main.leo");
let input_bytes = include_bytes!("inputs_fail_name.leo");
let input_string = String::from_utf8_lossy(input_bytes);
let mut program = parse_program(program_bytes).unwrap();
let error = program.parse_inputs(&PathBuf::new(), &input_string).unwrap_err();
fail_input_parser(error);
}
#[test]
fn test_inputs_fail_type() {
let program_bytes = include_bytes!("main.leo");
let input_bytes = include_bytes!("inputs_fail_type.leo");
let input_string = String::from_utf8_lossy(input_bytes);
let mut program = parse_program(program_bytes).unwrap();
let error = program.parse_inputs(&PathBuf::new(), &input_string).unwrap_err();
fail_input_parser(error);
}
#[test]
fn test_inputs_fail_visibility() {
let program_bytes = include_bytes!("main.leo");
let input_bytes = include_bytes!("inputs_fail_visibility.leo");
let input_string = String::from_utf8_lossy(input_bytes);
let mut program = parse_program(program_bytes).unwrap();
let error = program.parse_inputs(&PathBuf::new(), &input_string).unwrap_err();
fail_input_parser(error);
}
#[test]
fn test_inputs_multiple() {
let program_bytes = include_bytes!("main_multiple.leo");
let input_bytes = include_bytes!("inputs_multiple.leo");
let input_string = String::from_utf8_lossy(input_bytes);
let mut program = parse_program(program_bytes).unwrap();
program.parse_inputs(&PathBuf::new(), &input_string).unwrap();
output_true(program);
}

View File

@ -5,6 +5,7 @@ pub mod field;
pub mod function;
pub mod group;
pub mod import;
pub mod inputs;
pub mod integers;
pub mod mutability;
pub mod statements;
@ -54,11 +55,11 @@ pub(crate) fn parse_program(bytes: &[u8]) -> Result<EdwardsTestCompiler, Compile
}
pub(crate) fn parse_inputs(bytes: &[u8]) -> Result<EdwardsTestCompiler, CompilerError> {
let program_string = String::from_utf8_lossy(bytes);
let inputs_string = String::from_utf8_lossy(bytes);
let mut compiler = EdwardsTestCompiler::new();
compiler.parse_inputs(&PathBuf::new(), &program_string)?;
compiler.parse_inputs(&PathBuf::new(), &inputs_string)?;
Ok(compiler)
}

View File

@ -11,6 +11,9 @@ pub enum InputParserError {
#[error("expected type {}, got {}", _0, _1)]
IncompatibleTypes(String, String),
#[error("Program input value {} not found", _0)]
InputNotFound(String),
#[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf),

View File

@ -17,3 +17,13 @@ impl<'ast> ArrayType<'ast> {
self.dimensions.pop()
}
}
impl<'ast> std::fmt::Display for ArrayType<'ast> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self._type)?;
for row in &self.dimensions {
write!(f, "[{}]", row)?;
}
write!(f, "")
}
}

View File

@ -17,7 +17,7 @@ pub enum DataType {
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DataType::Integer(_) => write!(f, "integer"),
DataType::Integer(ref integer) => write!(f, "{}", integer),
DataType::Field(_) => write!(f, "field"),
DataType::Group(_) => write!(f, "group"),
DataType::Boolean(_) => write!(f, "bool"),

View File

@ -31,3 +31,15 @@ pub struct U64Type {}
#[derive(Clone, Debug, FromPest, PartialEq, Eq)]
#[pest_ast(rule(Rule::type_u128))]
pub struct U128Type {}
impl std::fmt::Display for IntegerType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IntegerType::U8Type(_) => write!(f, "u8"),
IntegerType::U16Type(_) => write!(f, "u16"),
IntegerType::U32Type(_) => write!(f, "u32"),
IntegerType::U64Type(_) => write!(f, "u64"),
IntegerType::U128Type(_) => write!(f, "u128"),
}
}
}

View File

@ -13,8 +13,8 @@ pub enum Type<'ast> {
impl<'ast> fmt::Display for Type<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Basic(ref _type) => write!(f, "basic"),
Type::Array(ref _type) => write!(f, "array"),
Type::Basic(ref basic) => write!(f, "{}", basic),
Type::Array(ref array) => write!(f, "{}", array),
}
}
}

View File

@ -28,8 +28,8 @@ impl<'ast> From<AstFunctionInput<'ast>> for FunctionInput {
}
}
impl fmt::Display for FunctionInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl FunctionInput {
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
// mut var: private bool
if self.mutable {
write!(f, "mut ")?;
@ -43,3 +43,15 @@ impl fmt::Display for FunctionInput {
write!(f, "{}", self._type)
}
}
impl fmt::Display for FunctionInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl fmt::Debug for FunctionInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}

View File

@ -1,6 +1,7 @@
use crate::InputValue;
use crate::{FunctionInput, InputValue};
use leo_inputs::{common::visibility::Visibility, files::File, InputParserError};
use leo_inputs::common::Private;
use snarkos_models::curves::PairingEngine;
#[derive(Clone)]
@ -29,20 +30,43 @@ impl Inputs {
self.program_inputs = vec![None; size];
}
pub fn from_inputs_file(file: File) -> Result<Self, InputParserError> {
pub fn from_inputs_file(file: File, expected_inputs: Vec<FunctionInput>) -> Result<Self, InputParserError> {
let mut private = vec![];
let mut public = vec![];
for section in file.sections.into_iter() {
for assignment in section.assignments.into_iter() {
let value = InputValue::from_expression(assignment.parameter.type_, assignment.expression)?;
if let Some(Visibility::Public(_)) = assignment.parameter.visibility {
// Collect public inputs here
public.push(value.clone());
}
if section.header.name.value.eq("main") {
for input in &expected_inputs {
// find input with matching name
let matched_input = section.assignments.clone().into_iter().find(|assignment| {
let visibility = assignment
.parameter
.visibility
.as_ref()
.map_or(true, |visibility| visibility.eq(&Visibility::Private(Private {})));
// push value to vector
private.push(Some(value));
// name match
assignment.parameter.variable.value.eq(&input.identifier.name)
// visibility match
&& visibility.eq(&input.private)
// type match
&& assignment.parameter.type_.to_string().eq(&input._type.to_string())
});
match matched_input {
Some(assignment) => {
let value = InputValue::from_expression(assignment.parameter.type_, assignment.expression)?;
if let Some(Visibility::Public(_)) = assignment.parameter.visibility {
// Collect public inputs here
public.push(value.clone());
}
// push value to vector
private.push(Some(value));
}
None => return Err(InputParserError::InputNotFound(input.to_string())),
}
}
}
}

View File

@ -1,7 +1,7 @@
//! A typed Leo program consists of import, circuit, and function definitions.
//! Each defined type consists of typed statements and expressions.
use crate::{Circuit, Function, Identifier, Import, TestFunction};
use crate::{Circuit, Function, FunctionInput, Identifier, Import, TestFunction};
use leo_ast::files::File;
use std::collections::HashMap;
@ -10,7 +10,7 @@ use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Program {
pub name: Identifier,
pub num_parameters: usize,
pub expected_inputs: Vec<FunctionInput>,
pub imports: Vec<Import>,
pub circuits: HashMap<Identifier, Circuit>,
pub functions: HashMap<Identifier, Function>,
@ -30,7 +30,7 @@ impl<'ast> Program {
let mut circuits = HashMap::new();
let mut functions = HashMap::new();
let mut tests = HashMap::new();
let mut num_parameters = 0usize;
let mut expected_inputs = vec![];
file.circuits.into_iter().for_each(|circuit| {
circuits.insert(Identifier::from(circuit.identifier.clone()), Circuit::from(circuit));
@ -49,12 +49,12 @@ impl<'ast> Program {
});
if let Some(main_function) = functions.get(&Identifier::new("main".into())) {
num_parameters = main_function.inputs.len();
expected_inputs = main_function.inputs.clone();
}
Self {
name: Identifier::new(name),
num_parameters,
expected_inputs,
imports,
circuits,
functions,
@ -67,7 +67,7 @@ impl Program {
pub fn new() -> Self {
Self {
name: Identifier::new("".into()),
num_parameters: 0,
expected_inputs: vec![],
imports: vec![],
circuits: HashMap::new(),
functions: HashMap::new(),