diff --git a/README.md b/README.md index 9728a064e7..50806f0b87 100644 --- a/README.md +++ b/README.md @@ -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` diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index b177bf67af..fa3a77ff5f 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -103,7 +103,7 @@ impl> Compiler { 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> Compiler { 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(()) } diff --git a/compiler/tests/inputs/inputs.leo b/compiler/tests/inputs/inputs.leo new file mode 100644 index 0000000000..e47a66d09f --- /dev/null +++ b/compiler/tests/inputs/inputs.leo @@ -0,0 +1,2 @@ +[main] +b: private bool = true; \ No newline at end of file diff --git a/compiler/tests/inputs/inputs_fail_name.leo b/compiler/tests/inputs/inputs_fail_name.leo new file mode 100644 index 0000000000..42beb0b509 --- /dev/null +++ b/compiler/tests/inputs/inputs_fail_name.leo @@ -0,0 +1,2 @@ +[main] +bad_name: private bool = true; \ No newline at end of file diff --git a/compiler/tests/inputs/inputs_fail_type.leo b/compiler/tests/inputs/inputs_fail_type.leo new file mode 100644 index 0000000000..1c5db3bfb1 --- /dev/null +++ b/compiler/tests/inputs/inputs_fail_type.leo @@ -0,0 +1,2 @@ +[main] +b: private u8 = 1; \ No newline at end of file diff --git a/compiler/tests/inputs/inputs_fail_visibility.leo b/compiler/tests/inputs/inputs_fail_visibility.leo new file mode 100644 index 0000000000..81f53cf9ed --- /dev/null +++ b/compiler/tests/inputs/inputs_fail_visibility.leo @@ -0,0 +1,2 @@ +[main] +b: public bool = true; \ No newline at end of file diff --git a/compiler/tests/inputs/inputs_multiple.leo b/compiler/tests/inputs/inputs_multiple.leo new file mode 100644 index 0000000000..32ee99b6cf --- /dev/null +++ b/compiler/tests/inputs/inputs_multiple.leo @@ -0,0 +1,3 @@ +[main] +b: private bool = true; +a: public bool = false; \ No newline at end of file diff --git a/compiler/tests/inputs/main.leo b/compiler/tests/inputs/main.leo new file mode 100644 index 0000000000..54eb4940dc --- /dev/null +++ b/compiler/tests/inputs/main.leo @@ -0,0 +1,3 @@ +function main(b: private bool) -> bool { + return b +} \ No newline at end of file diff --git a/compiler/tests/inputs/main_multiple.leo b/compiler/tests/inputs/main_multiple.leo new file mode 100644 index 0000000000..24f2cbe84d --- /dev/null +++ b/compiler/tests/inputs/main_multiple.leo @@ -0,0 +1,3 @@ +function main(a: public bool, b: private bool) -> bool { + return a || b +} \ No newline at end of file diff --git a/compiler/tests/inputs/mod.rs b/compiler/tests/inputs/mod.rs new file mode 100644 index 0000000000..424acd6378 --- /dev/null +++ b/compiler/tests/inputs/mod.rs @@ -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); +} diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index 283afeb561..1822a963f8 100644 --- a/compiler/tests/mod.rs +++ b/compiler/tests/mod.rs @@ -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 Result { - 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) } diff --git a/leo-inputs/src/errors/parser.rs b/leo-inputs/src/errors/parser.rs index a821fc4f63..4409b9828d 100644 --- a/leo-inputs/src/errors/parser.rs +++ b/leo-inputs/src/errors/parser.rs @@ -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), diff --git a/leo-inputs/src/types/array_type.rs b/leo-inputs/src/types/array_type.rs index b73cb53fea..fd1ca18dbe 100644 --- a/leo-inputs/src/types/array_type.rs +++ b/leo-inputs/src/types/array_type.rs @@ -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, "") + } +} diff --git a/leo-inputs/src/types/data_type.rs b/leo-inputs/src/types/data_type.rs index 49ad917eac..d675c59e6e 100644 --- a/leo-inputs/src/types/data_type.rs +++ b/leo-inputs/src/types/data_type.rs @@ -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"), diff --git a/leo-inputs/src/types/integer_type.rs b/leo-inputs/src/types/integer_type.rs index 504a1c3706..ed2b520d1c 100644 --- a/leo-inputs/src/types/integer_type.rs +++ b/leo-inputs/src/types/integer_type.rs @@ -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"), + } + } +} diff --git a/leo-inputs/src/types/type_.rs b/leo-inputs/src/types/type_.rs index f6ea8e8933..a7a823ecab 100644 --- a/leo-inputs/src/types/type_.rs +++ b/leo-inputs/src/types/type_.rs @@ -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), } } } diff --git a/types/src/functions/function_input.rs b/types/src/functions/function_input.rs index 636b622d7a..48fbbbed35 100644 --- a/types/src/functions/function_input.rs +++ b/types/src/functions/function_input.rs @@ -28,8 +28,8 @@ impl<'ast> From> 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) + } +} diff --git a/types/src/inputs/inputs.rs b/types/src/inputs/inputs.rs index dc08c30e98..d5b2b8b701 100644 --- a/types/src/inputs/inputs.rs +++ b/types/src/inputs/inputs.rs @@ -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 { + pub fn from_inputs_file(file: File, expected_inputs: Vec) -> Result { 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())), + } + } } } diff --git a/types/src/program.rs b/types/src/program.rs index c14adb9533..3bbad00d18 100644 --- a/types/src/program.rs +++ b/types/src/program.rs @@ -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, pub imports: Vec, pub circuits: HashMap, pub functions: HashMap, @@ -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(),