mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-13 06:43:01 +03:00
enforce name, type, visibility of inputs. add tests
This commit is contained in:
parent
b44e336cb6
commit
3f668422fd
@ -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`
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
2
compiler/tests/inputs/inputs.leo
Normal file
2
compiler/tests/inputs/inputs.leo
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
b: private bool = true;
|
2
compiler/tests/inputs/inputs_fail_name.leo
Normal file
2
compiler/tests/inputs/inputs_fail_name.leo
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
bad_name: private bool = true;
|
2
compiler/tests/inputs/inputs_fail_type.leo
Normal file
2
compiler/tests/inputs/inputs_fail_type.leo
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
b: private u8 = 1;
|
2
compiler/tests/inputs/inputs_fail_visibility.leo
Normal file
2
compiler/tests/inputs/inputs_fail_visibility.leo
Normal file
@ -0,0 +1,2 @@
|
||||
[main]
|
||||
b: public bool = true;
|
3
compiler/tests/inputs/inputs_multiple.leo
Normal file
3
compiler/tests/inputs/inputs_multiple.leo
Normal file
@ -0,0 +1,3 @@
|
||||
[main]
|
||||
b: private bool = true;
|
||||
a: public bool = false;
|
3
compiler/tests/inputs/main.leo
Normal file
3
compiler/tests/inputs/main.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(b: private bool) -> bool {
|
||||
return b
|
||||
}
|
3
compiler/tests/inputs/main_multiple.leo
Normal file
3
compiler/tests/inputs/main_multiple.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: public bool, b: private bool) -> bool {
|
||||
return a || b
|
||||
}
|
72
compiler/tests/inputs/mod.rs
Normal file
72
compiler/tests/inputs/mod.rs
Normal 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);
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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, "")
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
Loading…
Reference in New Issue
Block a user