diff --git a/.gitignore b/.gitignore index e995008d72..a2f8c9699b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +/tmp/ **.idea/ outputs/ *.DS_Store diff --git a/Cargo.lock b/Cargo.lock index d769545adc..fc008fd0df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -515,6 +515,7 @@ dependencies = [ "env_logger", "from-pest", "leo-compiler", + "leo-inputs", "log", "rand", "rand_core", @@ -550,6 +551,7 @@ version = "0.1.0" dependencies = [ "hex", "leo-ast", + "leo-inputs", "leo-types", "log", "rand", @@ -562,11 +564,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "leo-inputs" +version = "0.1.0" +dependencies = [ + "from-pest", + "pest", + "pest-ast", + "pest_derive", + "snarkos-algorithms", + "snarkos-curves", + "snarkos-errors", + "snarkos-gadgets", + "snarkos-models", + "thiserror", +] + [[package]] name = "leo-types" version = "0.1.0" dependencies = [ "leo-ast", + "leo-inputs", "snarkos-errors", "snarkos-models", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index ccffb94921..3982a1f22c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,11 @@ name = "leo" path = "leo/main.rs" [workspace] -members = [ "ast", "compiler", "types" ] +members = [ "ast", "compiler", "leo-inputs", "types" ] [dependencies] leo-compiler = { path = "compiler", version = "0.1.0" } +leo-inputs = { path = "leo-inputs", version = "0.1.0"} snarkos-algorithms = { path = "../snarkOS/algorithms", version = "0.8.0" } snarkos-curves = { path = "../snarkOS/curves", version = "0.8.0" } diff --git a/README.md b/README.md index 4e5dca0a8b..50806f0b87 100644 --- a/README.md +++ b/README.md @@ -381,6 +381,49 @@ test function expect_fail() { } ``` +# Leo Inputs + +Public and private inputs for a Leo program are specified in the `inputs/` directory. The syntax for an input file is a limited subset of the Leo program syntax. The default inputs file is `inputs/inputs.leo`. + +## 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. + +`inputs/inputs.leo` + +```rust +[main] // <- section header +a: private u32 = 1; // <- private input +b: public u32 = 2; // <- public input +``` + +`src/main.leo` + +```rust +function main(a: private u32, b: public u32) -> u32 { + let c: u32 = a + b; + return c +} +``` + +## Input Definitions + +### Supported types +```rust +[main] +a: bool = true; // <- booleans +b: u8 = 2; // <- integers +c: field = 0; // <- fields +d: group = (0, 1)group // <- group tuples +``` + +### Arrays +```rust +[main] +a: private u8[4] = [0u8; 4]; // <- single +b: private u8[2][3] = [[0u8; 2]; 3]; // <- multi-dimensional +``` # Leo CLI @@ -395,6 +438,7 @@ leo new {$NAME} This will create a new directory with a given package name. The new package will have a directory structure as follows: ``` - inputs # Your program inputs + - inputs.leo # Your program inputs for main.leo - outputs # Your program outputs - src - lib.leo # Your program library @@ -455,6 +499,8 @@ leo prove ``` Leo starts by checking the `target` directory for an existing `.leo.pk` file. If it doesn't exist, it will proceed to run `leo setup` and then continue. +Next any input files in the `inputs` directory are parsed and all input values are passed to the program. + Once again, Leo uses cryptographic randomness from your machine to produce the proof. The proof is stored in the `target` directory as `.leo.proof`: ``` diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 6c5f145b0d..4a7c2e1ce3 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] leo-ast = { path = "../ast", version = "0.1.0" } leo-types = { path = "../types", version = "0.1.0" } +leo-inputs = { path = "../leo-inputs", version = "0.1.0" } snarkos-curves = { path = "../../snarkOS/curves", version = "0.8.0" } snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0" } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index cf14be756a..fa3a77ff5f 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -6,7 +6,8 @@ use crate::{ GroupType, }; use leo_ast::LeoParser; -use leo_types::{InputValue, Program}; +use leo_inputs::LeoInputsParser; +use leo_types::{InputValue, Inputs, Program}; use snarkos_errors::gadgets::SynthesisError; use snarkos_models::{ @@ -15,6 +16,7 @@ use snarkos_models::{ }; use sha2::{Digest, Sha256}; +use snarkos_models::curves::PairingEngine; use std::{fs, marker::PhantomData, path::PathBuf}; #[derive(Clone)] @@ -22,7 +24,7 @@ pub struct Compiler> { package_name: String, main_file_path: PathBuf, program: Program, - program_inputs: Vec>, + program_inputs: Inputs, output: Option>, _engine: PhantomData, } @@ -33,7 +35,7 @@ impl> Compiler { package_name: "".to_string(), main_file_path: PathBuf::new(), program: Program::new(), - program_inputs: vec![], + program_inputs: Inputs::new(), output: None, _engine: PhantomData, } @@ -44,7 +46,7 @@ impl> Compiler { package_name, main_file_path, program: Program::new(), - program_inputs: vec![], + program_inputs: Inputs::new(), output: None, _engine: PhantomData, }; @@ -57,7 +59,11 @@ impl> Compiler { } pub fn set_inputs(&mut self, program_inputs: Vec>) { - self.program_inputs = program_inputs; + self.program_inputs.set_inputs(program_inputs); + } + + pub fn get_public_inputs(&self) -> Result, CompilerError> { + Ok(self.program_inputs.get_public_inputs::()?) } pub fn checksum(&self) -> Result { @@ -77,7 +83,7 @@ impl> Compiler { self, cs: &mut CS, ) -> Result, CompilerError> { - generate_constraints(cs, self.program, self.program_inputs) + generate_constraints(cs, self.program, self.program_inputs.get_inputs()) } pub fn compile_test_constraints(self, cs: &mut TestConstraintSystem) -> Result<(), CompilerError> { @@ -86,8 +92,7 @@ impl> Compiler { fn load_program(&mut self) -> Result { // Load the program syntax tree from the file path - let file_path = &self.main_file_path; - Ok(LeoParser::load_file(file_path)?) + Ok(LeoParser::load_file(&self.main_file_path)?) } pub fn parse_program(&mut self, program_string: &str) -> Result<(), CompilerError> { @@ -98,17 +103,26 @@ impl> Compiler { let package_name = self.package_name.clone(); self.program = Program::from(syntax_tree, package_name); - self.program_inputs = vec![None; self.program.num_parameters]; + self.program_inputs.set_inputs_size(self.program.expected_inputs.len()); log::debug!("Program parsing complete\n{:#?}", self.program); Ok(()) } + + pub fn parse_inputs(&mut self, input_file_path: &PathBuf, input_file_string: &str) -> Result<(), CompilerError> { + 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.expected_inputs.clone())?; + + Ok(()) + } } impl> ConstraintSynthesizer for Compiler { fn generate_constraints>(self, cs: &mut CS) -> Result<(), SynthesisError> { - let _result = generate_constraints::<_, G, _>(cs, self.program, self.program_inputs).unwrap(); + let _result = generate_constraints::<_, G, _>(cs, self.program, self.program_inputs.get_inputs()).unwrap(); // Write results to file or something diff --git a/compiler/src/errors/compiler.rs b/compiler/src/errors/compiler.rs index 593015cfb3..d6ee381509 100644 --- a/compiler/src/errors/compiler.rs +++ b/compiler/src/errors/compiler.rs @@ -1,5 +1,6 @@ use crate::errors::{FunctionError, ImportError}; use leo_ast::{ParserError, SyntaxError}; +use leo_inputs::InputParserError; use leo_types::IntegerError; use std::{io, path::PathBuf}; @@ -15,6 +16,9 @@ pub enum CompilerError { #[error("{}", _0)] ImportError(#[from] ImportError), + #[error("{}", _0)] + InputParserError(#[from] InputParserError), + #[error("{}", _0)] IntegerError(#[from] IntegerError), diff --git a/compiler/src/leo-inputs.pest b/compiler/src/leo-inputs.pest deleted file mode 100644 index 0ba380ac4f..0000000000 --- a/compiler/src/leo-inputs.pest +++ /dev/null @@ -1,72 +0,0 @@ -/// Visibility - -visibility_public = { "public" } -visibility_private = { "private" } -visibility = { visibility_public | visibility_private } - -/// Types - -ty_u32 = {"u32"} -ty_field = {"fe"} -ty_bool = {"bool"} -ty_basic = { ty_u32 | ty_field | ty_bool } -ty_struct = { variable } -ty_array = {ty_basic ~ ("[" ~ value ~ "]")+ } -ty = { ty_array | ty_basic | ty_struct } - -/// Values - -value_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } -value_u32 = { value_number ~ ty_u32? } -value_field = { value_number ~ ty_field } -value_boolean = { "true" | "false" } -value = { value_field | value_boolean | value_u32 } - -/// Variables - -protected_name = { visibility | "return" } -variable = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } - -/// Arrays - -inline_array_inner = _{(expression_term ~ ("," ~ NEWLINE* ~ expression_term)*)?} -expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"} -expression_array_initializer = { "[" ~ expression_term ~ ";" ~ value ~ "]" } - -/// Structs - -inline_struct_member = { variable ~ ":" ~ expression_term } -inline_struct_member_list = _{(inline_struct_member ~ ("," ~ NEWLINE* ~ inline_struct_member)*)? ~ ","? } -expression_inline_struct = { variable ~ "{" ~ NEWLINE* ~ inline_struct_member_list ~ NEWLINE* ~ "}" } - -/// Expressions - -expression_primitive = { value | variable } -expression_term = { - expression_inline_struct - | expression_primitive - | expression_array_inline - | expression_array_initializer -} - -/// Functions - -parameter = { variable ~ ":" ~ visibility? ~ ty } - -function_name = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } - -/// Section - -header = { "[" ~ function_name ~ "]" } -assignment = { parameter ~ "=" ~ expression_term } - -section = { header ~ NEWLINE+ ~ assignment* ~ NEWLINE* } - -/// Utilities - -COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) } -WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* } - -/// Program File - -file = { SOI ~ NEWLINE* ~ section* ~ NEWLINE* ~ EOI } diff --git a/compiler/tests/array/mod.rs b/compiler/tests/array/mod.rs index f38d620719..2df023642b 100644 --- a/compiler/tests/array/mod.rs +++ b/compiler/tests/array/mod.rs @@ -5,6 +5,7 @@ use leo_compiler::{ }; use leo_types::{InputValue, Integer, IntegerError}; +use leo_inputs::types::{IntegerType, U32Type}; use snarkos_models::gadgets::utilities::uint::UInt32; // [1, 1, 1] @@ -52,6 +53,10 @@ fn fail_synthesis(program: EdwardsTestCompiler) { } } +pub(crate) fn input_value_u32_one() -> InputValue { + InputValue::Integer(IntegerType::U32Type(U32Type {}), 1) +} + // Expressions #[test] @@ -101,7 +106,7 @@ fn test_input_array() { let bytes = include_bytes!("input.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Array(vec![InputValue::Integer(1u128); 3]))]); + program.set_inputs(vec![Some(InputValue::Array(vec![input_value_u32_one(); 3]))]); output_ones(program) } @@ -111,7 +116,7 @@ fn test_input_array_fail() { let bytes = include_bytes!("input.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Integer(1u128))]); + program.set_inputs(vec![Some(input_value_u32_one())]); fail_array(program); } diff --git a/compiler/tests/boolean/mod.rs b/compiler/tests/boolean/mod.rs index 5ad4313719..01b428e755 100644 --- a/compiler/tests/boolean/mod.rs +++ b/compiler/tests/boolean/mod.rs @@ -3,8 +3,8 @@ use leo_compiler::{ errors::{BooleanError, CompilerError, ExpressionError, FunctionError, StatementError}, ConstrainedValue, }; -use leo_types::InputValue; +use crate::array::input_value_u32_one; use snarkos_models::gadgets::utilities::boolean::Boolean; pub fn output_expected_boolean(program: EdwardsTestCompiler, boolean: bool) { @@ -76,7 +76,7 @@ fn test_input_bool_field() { let bytes = include_bytes!("input_bool.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Integer(1u128))]); + program.set_inputs(vec![Some(input_value_u32_one())]); fail_boolean(program); } 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/integers/macros.rs b/compiler/tests/integers/macros.rs index 96f414d3ec..f322113f39 100644 --- a/compiler/tests/integers/macros.rs +++ b/compiler/tests/integers/macros.rs @@ -1,5 +1,5 @@ macro_rules! test_uint { - ($name: ident, $_type: ty, $gadget: ty) => { + ($name: ident, $_type: ty, $integer_type: expr, $gadget: ty) => { pub struct $name {} impl $name { @@ -31,7 +31,7 @@ macro_rules! test_uint { let bytes = include_bytes!("input.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Integer(num as u128))]); + program.set_inputs(vec![Some(InputValue::Integer($integer_type, num as u128))]); output_expected_allocated(program, expected); @@ -61,8 +61,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program, sum_allocated); @@ -83,8 +83,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program, difference_allocated); @@ -105,8 +105,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program, product_allocated); @@ -127,8 +127,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program, quotient_allocated); @@ -150,8 +150,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program, result_allocated); @@ -167,8 +167,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); output_true(program); @@ -181,8 +181,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_boolean(program, result); @@ -198,8 +198,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); output_true(program); @@ -212,8 +212,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_boolean(program, result); @@ -229,8 +229,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); output_false(program); @@ -243,8 +243,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_boolean(program, result); @@ -260,8 +260,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); output_true(program); @@ -274,8 +274,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_boolean(program, result); @@ -291,8 +291,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); output_false(program); @@ -305,8 +305,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_boolean(program, result); @@ -322,8 +322,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), ]); let _ = get_output(program); @@ -338,8 +338,8 @@ macro_rules! test_uint { let mut program = parse_program(bytes).unwrap(); program.set_inputs(vec![ - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); let mut cs = TestConstraintSystem::::new(); @@ -363,8 +363,8 @@ macro_rules! test_uint { // true -> field 1 program_1.set_inputs(vec![ Some(InputValue::Boolean(true)), - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program_1, g1); @@ -372,8 +372,8 @@ macro_rules! test_uint { // false -> field 2 program_2.set_inputs(vec![ Some(InputValue::Boolean(false)), - Some(InputValue::Integer(r1 as u128)), - Some(InputValue::Integer(r2 as u128)), + Some(InputValue::Integer($integer_type, r1 as u128)), + Some(InputValue::Integer($integer_type, r2 as u128)), ]); output_expected_allocated(program_2, g2); diff --git a/compiler/tests/integers/u128/mod.rs b/compiler/tests/integers/u128/mod.rs index b2d41c0bd6..ad5cc58bd9 100644 --- a/compiler/tests/integers/u128/mod.rs +++ b/compiler/tests/integers/u128/mod.rs @@ -7,6 +7,7 @@ use crate::{ EdwardsTestCompiler, }; use leo_compiler::ConstrainedValue; +use leo_inputs::types::{IntegerType, U128Type}; use leo_types::{InputValue, Integer}; use snarkos_curves::edwards_bls12::Fq; @@ -30,7 +31,7 @@ fn output_expected_allocated(program: EdwardsTestCompiler, expected: UInt128) { #[test] #[ignore] // temporarily ignore memory expensive tests for travis fn test_u128() { - test_uint!(TestU128, u128, UInt128); + test_uint!(TestU128, u128, IntegerType::U128Type(U128Type {}), UInt128); TestU128::test_min(std::u128::MIN); TestU128::test_max(std::u128::MAX); diff --git a/compiler/tests/integers/u16/mod.rs b/compiler/tests/integers/u16/mod.rs index 9a5510d741..e6aa8b1060 100644 --- a/compiler/tests/integers/u16/mod.rs +++ b/compiler/tests/integers/u16/mod.rs @@ -7,6 +7,7 @@ use crate::{ EdwardsTestCompiler, }; use leo_compiler::ConstrainedValue; +use leo_inputs::types::{IntegerType, U16Type}; use leo_types::{InputValue, Integer}; use snarkos_curves::edwards_bls12::Fq; @@ -29,7 +30,7 @@ fn output_expected_allocated(program: EdwardsTestCompiler, expected: UInt16) { #[test] fn test_u16() { - test_uint!(Testu16, u16, UInt16); + test_uint!(Testu16, u16, IntegerType::U16Type(U16Type {}), UInt16); Testu16::test_min(std::u16::MIN); Testu16::test_max(std::u16::MAX); diff --git a/compiler/tests/integers/u32/mod.rs b/compiler/tests/integers/u32/mod.rs index 5f9d39f24c..d9b02855c7 100644 --- a/compiler/tests/integers/u32/mod.rs +++ b/compiler/tests/integers/u32/mod.rs @@ -7,6 +7,7 @@ use crate::{ EdwardsTestCompiler, }; use leo_compiler::ConstrainedValue; +use leo_inputs::types::{IntegerType, U32Type}; use leo_types::{InputValue, Integer}; use snarkos_curves::edwards_bls12::Fq; @@ -47,7 +48,7 @@ pub(crate) fn output_one(program: EdwardsTestCompiler) { #[test] fn test_u32() { - test_uint!(TestU32, u32, UInt32); + test_uint!(TestU32, u32, IntegerType::U32Type(U32Type {}), UInt32); TestU32::test_min(std::u32::MIN); TestU32::test_max(std::u32::MAX); diff --git a/compiler/tests/integers/u64/mod.rs b/compiler/tests/integers/u64/mod.rs index d31fb904a6..87d73ddcfe 100644 --- a/compiler/tests/integers/u64/mod.rs +++ b/compiler/tests/integers/u64/mod.rs @@ -7,6 +7,7 @@ use crate::{ EdwardsTestCompiler, }; use leo_compiler::ConstrainedValue; +use leo_inputs::types::{IntegerType, U64Type}; use leo_types::{InputValue, Integer}; use snarkos_curves::edwards_bls12::Fq; @@ -30,7 +31,7 @@ fn output_expected_allocated(program: EdwardsTestCompiler, expected: UInt64) { #[test] #[ignore] //temporarily ignore memory expensive tests for travis fn test_u64() { - test_uint!(Testu64, u64, UInt64); + test_uint!(Testu64, u64, IntegerType::U64Type(U64Type {}), UInt64); Testu64::test_min(std::u64::MIN); Testu64::test_max(std::u64::MAX); diff --git a/compiler/tests/integers/u8/mod.rs b/compiler/tests/integers/u8/mod.rs index 0fa87e8925..40796075f8 100644 --- a/compiler/tests/integers/u8/mod.rs +++ b/compiler/tests/integers/u8/mod.rs @@ -7,6 +7,7 @@ use crate::{ EdwardsTestCompiler, }; use leo_compiler::ConstrainedValue; +use leo_inputs::types::{IntegerType, U8Type}; use leo_types::{InputValue, Integer}; use snarkos_curves::edwards_bls12::Fq; @@ -29,7 +30,7 @@ fn output_expected_allocated(program: EdwardsTestCompiler, expected: UInt8) { #[test] fn test_u8() { - test_uint!(Testu8, u8, UInt8); + test_uint!(Testu8, u8, IntegerType::U8Type(U8Type {}), UInt8); Testu8::test_min(std::u8::MIN); Testu8::test_max(std::u8::MAX); diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index 9898b6c53d..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; @@ -19,6 +20,7 @@ use leo_compiler::{ use snarkos_curves::edwards_bls12::Fq; use snarkos_models::gadgets::r1cs::TestConstraintSystem; +use std::path::PathBuf; pub type EdwardsTestCompiler = Compiler; pub type EdwardsConstrainedValue = ConstrainedValue; @@ -51,3 +53,13 @@ pub(crate) fn parse_program(bytes: &[u8]) -> Result Result { + let inputs_string = String::from_utf8_lossy(bytes); + + let mut compiler = EdwardsTestCompiler::new(); + + compiler.parse_inputs(&PathBuf::new(), &inputs_string)?; + + Ok(compiler) +} diff --git a/compiler/tests/mutability/mod.rs b/compiler/tests/mutability/mod.rs index 5a6defbfd8..e04ca08cae 100644 --- a/compiler/tests/mutability/mod.rs +++ b/compiler/tests/mutability/mod.rs @@ -3,8 +3,9 @@ use leo_compiler::{ errors::{CompilerError, FunctionError, StatementError}, ConstrainedValue, }; -use leo_types::{InputValue, Integer}; +use leo_types::Integer; +use crate::array::input_value_u32_one; use snarkos_curves::edwards_bls12::Fq; use snarkos_models::gadgets::{r1cs::TestConstraintSystem, utilities::uint::UInt32}; @@ -84,7 +85,7 @@ fn test_function_input() { let bytes = include_bytes!("function_input.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Integer(1))]); + program.set_inputs(vec![Some(input_value_u32_one())]); mut_fail(program); } @@ -93,6 +94,6 @@ fn test_function_input_mut() { let bytes = include_bytes!("function_input_mut.leo"); let mut program = parse_program(bytes).unwrap(); - program.set_inputs(vec![Some(InputValue::Integer(1))]); + program.set_inputs(vec![Some(input_value_u32_one())]); mut_success(program); } diff --git a/compiler/tests/syntax/inputs_semicolon.leo b/compiler/tests/syntax/inputs_semicolon.leo new file mode 100644 index 0000000000..9054049169 --- /dev/null +++ b/compiler/tests/syntax/inputs_semicolon.leo @@ -0,0 +1,2 @@ +[main] +a: private u32 = 5 \ No newline at end of file diff --git a/compiler/tests/syntax/mod.rs b/compiler/tests/syntax/mod.rs index 4cf5ec5f0b..7d5630760c 100644 --- a/compiler/tests/syntax/mod.rs +++ b/compiler/tests/syntax/mod.rs @@ -1,6 +1,7 @@ -use crate::parse_program; +use crate::{parse_inputs, parse_program}; use leo_ast::ParserError; use leo_compiler::errors::CompilerError; +use leo_inputs::InputParserError; #[test] fn test_semicolon() { @@ -12,3 +13,14 @@ fn test_semicolon() { _ => panic!("test_semicolon failed the wrong expected error, should be a ParserError"), } } + +#[test] +fn inputs_syntax_error() { + let bytes = include_bytes!("inputs_semicolon.leo"); + let error = parse_inputs(bytes).err().unwrap(); + + match error { + CompilerError::InputParserError(InputParserError::SyntaxError(_)) => {} + _ => panic!("inputs syntax error should be a ParserError"), + } +} diff --git a/leo-inputs/Cargo.toml b/leo-inputs/Cargo.toml new file mode 100644 index 0000000000..05285399f8 --- /dev/null +++ b/leo-inputs/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "leo-inputs" +version = "0.1.0" +authors = ["The Aleo Team "] +edition = "2018" + +[dependencies] +snarkos-algorithms = { path = "../../snarkOS/algorithms", version = "0.8.0" } +snarkos-curves = { path = "../../snarkOS/curves", version = "0.8.0" } +snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0" } +snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0" } +snarkos-models = { path = "../../snarkOS/models", version = "0.8.0" } + +from-pest = { version = "0.3.1" } +pest = { version = "2.0" } +pest-ast = { version = "0.3.3" } +pest_derive = { version = "2.0" } +thiserror = { version = "1.0" } diff --git a/leo-inputs/input.leo b/leo-inputs/input.leo new file mode 100644 index 0000000000..fce77b60c5 --- /dev/null +++ b/leo-inputs/input.leo @@ -0,0 +1,4 @@ +[main] +a: private u32 = 5; +b: public field = 1field; +c: private bool = true; \ No newline at end of file diff --git a/leo-inputs/src/assignments/assignment.rs b/leo-inputs/src/assignments/assignment.rs new file mode 100644 index 0000000000..0945a146a1 --- /dev/null +++ b/leo-inputs/src/assignments/assignment.rs @@ -0,0 +1,14 @@ +use crate::{ast::Rule, common::LineEnd, expressions::Expression, parameters::Parameter}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::assignment))] +pub struct Assignment<'ast> { + pub parameter: Parameter<'ast>, + pub expression: Expression<'ast>, + pub line_end: LineEnd, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/assignments/mod.rs b/leo-inputs/src/assignments/mod.rs new file mode 100644 index 0000000000..e02881fe86 --- /dev/null +++ b/leo-inputs/src/assignments/mod.rs @@ -0,0 +1,2 @@ +pub mod assignment; +pub use assignment::*; diff --git a/leo-inputs/src/ast.rs b/leo-inputs/src/ast.rs new file mode 100644 index 0000000000..9d82bbb91d --- /dev/null +++ b/leo-inputs/src/ast.rs @@ -0,0 +1,14 @@ +//! Abstract syntax tree (ast) representation from leo-inputs.pest. +use pest::{error::Error, iterators::Pairs, Parser, Span}; + +#[derive(Parser)] +#[grammar = "leo-inputs.pest"] +pub struct LanguageParser; + +pub fn parse(input: &str) -> Result, Error> { + LanguageParser::parse(Rule::file, input) +} + +pub fn span_into_string(span: Span) -> String { + span.as_str().to_string() +} diff --git a/leo-inputs/src/common/eoi.rs b/leo-inputs/src/common/eoi.rs new file mode 100644 index 0000000000..1293320f1a --- /dev/null +++ b/leo-inputs/src/common/eoi.rs @@ -0,0 +1,7 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::EOI))] +pub struct EOI; diff --git a/leo-inputs/src/common/identifier.rs b/leo-inputs/src/common/identifier.rs new file mode 100644 index 0000000000..35c74e30be --- /dev/null +++ b/leo-inputs/src/common/identifier.rs @@ -0,0 +1,20 @@ +use crate::ast::{span_into_string, Rule}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::identifier))] +pub struct Identifier<'ast> { + #[pest_ast(outer(with(span_into_string)))] + pub value: String, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for Identifier<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.value) + } +} diff --git a/leo-inputs/src/common/line_end.rs b/leo-inputs/src/common/line_end.rs new file mode 100644 index 0000000000..ee34060bd6 --- /dev/null +++ b/leo-inputs/src/common/line_end.rs @@ -0,0 +1,7 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::LINE_END))] +pub struct LineEnd; diff --git a/leo-inputs/src/common/mod.rs b/leo-inputs/src/common/mod.rs new file mode 100644 index 0000000000..b0b7155373 --- /dev/null +++ b/leo-inputs/src/common/mod.rs @@ -0,0 +1,11 @@ +pub mod eoi; +pub use eoi::*; + +pub mod identifier; +pub use identifier::*; + +pub mod line_end; +pub use line_end::*; + +pub mod visibility; +pub use visibility::*; diff --git a/leo-inputs/src/common/visibility.rs b/leo-inputs/src/common/visibility.rs new file mode 100644 index 0000000000..cd63bb1119 --- /dev/null +++ b/leo-inputs/src/common/visibility.rs @@ -0,0 +1,18 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::visibility))] +pub enum Visibility { + Public(Public), + Private(Private), +} + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::visibility_public))] +pub struct Public {} + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::visibility_private))] +pub struct Private {} diff --git a/leo-inputs/src/errors/mod.rs b/leo-inputs/src/errors/mod.rs new file mode 100644 index 0000000000..9d9164fa35 --- /dev/null +++ b/leo-inputs/src/errors/mod.rs @@ -0,0 +1,5 @@ +pub mod parser; +pub use parser::*; + +pub mod syntax; +pub use syntax::*; diff --git a/leo-inputs/src/errors/parser.rs b/leo-inputs/src/errors/parser.rs new file mode 100644 index 0000000000..4409b9828d --- /dev/null +++ b/leo-inputs/src/errors/parser.rs @@ -0,0 +1,43 @@ +use crate::{ast::Rule, errors::SyntaxError}; + +use pest::error::Error; +use std::{num::ParseIntError, path::PathBuf, str::ParseBoolError}; + +#[derive(Debug, Error)] +pub enum InputParserError { + #[error("expected array length {}, got {}", _0, _1)] + InvalidArrayLength(usize, usize), + + #[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), + + #[error("{}", _0)] + ParseBoolError(#[from] ParseBoolError), + + #[error("{}", _0)] + ParseIntError(#[from] ParseIntError), + + #[error("cannot parse {} as field", _0)] + ParseFieldError(String), + + #[error("{}", _0)] + SyntaxError(#[from] SyntaxError), + + #[error("Unable to construct abstract syntax tree")] + SyntaxTreeError, + + #[error("found an empty array dimension in type")] + UndefinedArrayDimension, +} + +impl From> for InputParserError { + fn from(error: Error) -> Self { + InputParserError::SyntaxError(SyntaxError::from(error)) + } +} diff --git a/leo-inputs/src/errors/syntax.rs b/leo-inputs/src/errors/syntax.rs new file mode 100644 index 0000000000..a436ec15b1 --- /dev/null +++ b/leo-inputs/src/errors/syntax.rs @@ -0,0 +1,29 @@ +use crate::ast::Rule; + +use pest::error::Error; + +#[derive(Debug, Error)] +pub enum SyntaxError { + #[error("aborting due to syntax error")] + Error, +} + +impl From> for SyntaxError { + fn from(mut error: Error) -> Self { + error = error.renamed_rules(|rule| match *rule { + Rule::LINE_END => "`;`".to_owned(), + Rule::type_integer => "`u32`".to_owned(), + Rule::type_field => "`field`".to_owned(), + Rule::type_group => "`group`".to_owned(), + Rule::file => "an import, circuit, or function".to_owned(), + Rule::identifier => "a variable name".to_owned(), + Rule::type_ => "a type".to_owned(), + + rule => format!("{:?}", rule), + }); + + println!("{}\n", error); + + SyntaxError::Error + } +} diff --git a/leo-inputs/src/expressions/array_initializer_expression.rs b/leo-inputs/src/expressions/array_initializer_expression.rs new file mode 100644 index 0000000000..ba9d86f3a0 --- /dev/null +++ b/leo-inputs/src/expressions/array_initializer_expression.rs @@ -0,0 +1,13 @@ +use crate::{ast::Rule, expressions::Expression, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::expression_array_initializer))] +pub struct ArrayInitializerExpression<'ast> { + pub expression: Box>, + pub count: NumberValue<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/expressions/array_inline_expression.rs b/leo-inputs/src/expressions/array_inline_expression.rs new file mode 100644 index 0000000000..bb25e12721 --- /dev/null +++ b/leo-inputs/src/expressions/array_inline_expression.rs @@ -0,0 +1,12 @@ +use crate::{ast::Rule, expressions::Expression}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::expression_array_inline))] +pub struct ArrayInlineExpression<'ast> { + pub expressions: Vec>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/expressions/expression.rs b/leo-inputs/src/expressions/expression.rs new file mode 100644 index 0000000000..29de72bf91 --- /dev/null +++ b/leo-inputs/src/expressions/expression.rs @@ -0,0 +1,32 @@ +use crate::{ast::Rule, expressions::*, values::Value}; + +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::expression))] +pub enum Expression<'ast> { + ArrayInline(ArrayInlineExpression<'ast>), + ArrayInitializer(ArrayInitializerExpression<'ast>), + Value(Value<'ast>), +} + +impl<'ast> fmt::Display for Expression<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Expression::Value(ref expression) => write!(f, "{}", expression), + Expression::ArrayInline(ref expression) => { + for (i, spread_or_expression) in expression.expressions.iter().enumerate() { + write!(f, "{}", spread_or_expression)?; + if i < expression.expressions.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, "") + } + Expression::ArrayInitializer(ref expression) => { + write!(f, "[{} ; {}]", expression.expression, expression.count) + } + } + } +} diff --git a/leo-inputs/src/expressions/mod.rs b/leo-inputs/src/expressions/mod.rs new file mode 100644 index 0000000000..621a7da70d --- /dev/null +++ b/leo-inputs/src/expressions/mod.rs @@ -0,0 +1,8 @@ +pub mod array_initializer_expression; +pub use array_initializer_expression::*; + +pub mod array_inline_expression; +pub use array_inline_expression::*; + +pub mod expression; +pub use expression::*; diff --git a/leo-inputs/src/files/file.rs b/leo-inputs/src/files/file.rs new file mode 100644 index 0000000000..1c6a1ddeb9 --- /dev/null +++ b/leo-inputs/src/files/file.rs @@ -0,0 +1,13 @@ +use crate::{ast::Rule, common::EOI, sections::Section}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::file))] +pub struct File<'ast> { + pub sections: Vec>, + pub eoi: EOI, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/files/mod.rs b/leo-inputs/src/files/mod.rs new file mode 100644 index 0000000000..de03a628a4 --- /dev/null +++ b/leo-inputs/src/files/mod.rs @@ -0,0 +1,2 @@ +pub mod file; +pub use file::*; diff --git a/leo-inputs/src/leo-inputs.pest b/leo-inputs/src/leo-inputs.pest new file mode 100644 index 0000000000..9be3f7eb80 --- /dev/null +++ b/leo-inputs/src/leo-inputs.pest @@ -0,0 +1,115 @@ +/// Common + +// Declared in common/identifier.rs +identifier = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } +protected_name = { visibility | "let" | "for"| "if" | "else" | "as" | "return" } + +// Declared in common/line_end.rs +LINE_END = { ";" ~ NEWLINE* } + +// Declared in common/visibility.rs +visibility = { visibility_public | visibility_private } +visibility_public = { "public" } +visibility_private = { "private" } + +/// Types + +// Declared in types/type_.rs +type_ = { type_array | type_data } + +// Declared in types/integer_type.rs +type_integer = { + type_u8 + | type_u16 + | type_u32 + | type_u64 + | type_u128 +} +type_u8 = { "u8" } +type_u16 = { "u16" } +type_u32 = { "u32" } +type_u64 = { "u64" } +type_u128 = { "u128" } + +// Declared in types/field_type.rs +type_field = { "field" } + +// Declared in types/group_type.rs +type_group = { "group" } + +// Declared in types/boolean_type.rs +type_boolean = { "bool" } + +// Declared in types/data_type.rs +type_data = { type_field | type_group | type_boolean | type_integer } + +// Declared in types/array_type.rs +type_array = { type_data ~ ("[" ~ value_number ~ "]")+ } + +/// Values + +// Declared in values/value.rs +value = { value_field | value_group | value_boolean | value_integer | value_implicit } + +// Declared in values/number_value.rs +value_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } + +// Declared in values/number_implicit_value.rs +value_implicit = { value_number } + +// Declared in values/integer_value.rs +value_integer = { value_number ~ type_integer } + +// Declared in values/boolean_value.rs +value_boolean = { "true" | "false" } + +// Declared in values/field_value.rs +value_field = { value_number ~ type_field } + +// Declared in values/group_value.rs +value_group = { group_tuple ~ type_group } +group_tuple = { "(" ~ NEWLINE* ~ value_number ~ "," ~ NEWLINE* ~ value_number ~ NEWLINE* ~")" } + +/// Expressions + +// Declared in expressions/array_initializer_expression.rs +expression_array_initializer = { "[" ~ expression ~ ";" ~ value_number ~ "]" } + +// Declared in expressions/array_inline_expression.rs +expression_array_inline = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]"} +inline_array_inner = _{ (expression ~ ("," ~ NEWLINE* ~ expression)*)? } + +// Declared in expressions/expression.rs +expression = { + expression_array_inline + | expression_array_initializer + | value +} + +/// Parameters + +// Declared in parameters/parameters.rs +parameter = { identifier ~ ":" ~ visibility? ~ type_ } + +/// Section + +// Declared in sections/section.rs +section = { header ~ NEWLINE+ ~ (assignment ~ NEWLINE*)* } + +// Declared in sections/header.rs +header = { "[" ~ identifier ~ "]" } + +/// Assignments + +// Declared in assignment/assignment.rs +assignment = { parameter ~ "=" ~ NEWLINE* ~ expression ~ LINE_END } + +/// Utilities + +COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) } +WHITESPACE = _{ " " | "\t" ~ (NEWLINE)* } + +/// Files + +// Declared in files/file.rs +file = { SOI ~ NEWLINE* ~ section* ~ NEWLINE* ~ EOI } diff --git a/leo-inputs/src/lib.rs b/leo-inputs/src/lib.rs new file mode 100644 index 0000000000..0680d48f62 --- /dev/null +++ b/leo-inputs/src/lib.rs @@ -0,0 +1,43 @@ +#[macro_use] +extern crate pest_derive; +#[macro_use] +extern crate thiserror; + +pub mod errors; +pub use errors::*; + +//extern crate from_pest; +pub mod assignments; +mod ast; +pub mod common; +pub mod expressions; +pub mod files; +pub mod parameters; +pub mod sections; +pub mod types; +pub mod values; + +use from_pest::FromPest; +use std::{fs, path::PathBuf}; + +pub struct LeoInputsParser; + +impl LeoInputsParser { + /// Reads in the given file path into a string. + pub fn load_file(file_path: &PathBuf) -> Result { + Ok(fs::read_to_string(file_path).map_err(|_| InputParserError::FileReadError(file_path.clone()))?) + } + + /// Parses the input file and constructs a syntax tree. + pub fn parse_file<'a>(file_path: &'a PathBuf, input_file: &'a str) -> Result, InputParserError> { + // Parse the file using leo.pest + let mut file = ast::parse(input_file) + .map_err(|error| InputParserError::from(error.with_path(file_path.to_str().unwrap())))?; + + // Build the abstract syntax tree + let syntax_tree = files::File::from_pest(&mut file).map_err(|_| InputParserError::SyntaxTreeError)?; + // println!("{:?}", syntax_tree); + + Ok(syntax_tree) + } +} diff --git a/leo-inputs/src/main.rs b/leo-inputs/src/main.rs new file mode 100644 index 0000000000..50dc3b1944 --- /dev/null +++ b/leo-inputs/src/main.rs @@ -0,0 +1,13 @@ +use leo_inputs::{self, LeoInputsParser}; + +use std::env::current_dir; + +fn main() { + let mut path = current_dir().unwrap(); + path.push("input.leo"); + + let input_file = &LeoInputsParser::load_file(&path).expect("cannot read file"); + let syntax_tree = LeoInputsParser::parse_file(&path, input_file).unwrap(); + + println!("tree: {:#?}", syntax_tree); +} diff --git a/leo-inputs/src/parameters/mod.rs b/leo-inputs/src/parameters/mod.rs new file mode 100644 index 0000000000..cf55dbc6dd --- /dev/null +++ b/leo-inputs/src/parameters/mod.rs @@ -0,0 +1,2 @@ +pub mod parameter; +pub use parameter::*; diff --git a/leo-inputs/src/parameters/parameter.rs b/leo-inputs/src/parameters/parameter.rs new file mode 100644 index 0000000000..a58e8cc339 --- /dev/null +++ b/leo-inputs/src/parameters/parameter.rs @@ -0,0 +1,18 @@ +use crate::{ + ast::Rule, + common::{Identifier, Visibility}, + types::Type, +}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::parameter))] +pub struct Parameter<'ast> { + pub variable: Identifier<'ast>, + pub visibility: Option, + pub type_: Type<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/sections/header.rs b/leo-inputs/src/sections/header.rs new file mode 100644 index 0000000000..5ecb08eb14 --- /dev/null +++ b/leo-inputs/src/sections/header.rs @@ -0,0 +1,12 @@ +use crate::{ast::Rule, common::Identifier}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::header))] +pub struct Header<'ast> { + pub name: Identifier<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/sections/mod.rs b/leo-inputs/src/sections/mod.rs new file mode 100644 index 0000000000..ad84b1fade --- /dev/null +++ b/leo-inputs/src/sections/mod.rs @@ -0,0 +1,5 @@ +pub mod header; +pub use header::*; + +pub mod section; +pub use section::*; diff --git a/leo-inputs/src/sections/section.rs b/leo-inputs/src/sections/section.rs new file mode 100644 index 0000000000..02186b2c61 --- /dev/null +++ b/leo-inputs/src/sections/section.rs @@ -0,0 +1,13 @@ +use crate::{assignments::Assignment, ast::Rule, sections::Header}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::section))] +pub struct Section<'ast> { + pub header: Header<'ast>, + pub assignments: Vec>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} diff --git a/leo-inputs/src/types/array_type.rs b/leo-inputs/src/types/array_type.rs new file mode 100644 index 0000000000..fd1ca18dbe --- /dev/null +++ b/leo-inputs/src/types/array_type.rs @@ -0,0 +1,29 @@ +use crate::{ast::Rule, types::DataType, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_array))] +pub struct ArrayType<'ast> { + pub _type: DataType, + pub dimensions: Vec>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> ArrayType<'ast> { + pub fn next_dimension(&mut self) -> Option> { + 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/boolean_type.rs b/leo-inputs/src/types/boolean_type.rs new file mode 100644 index 0000000000..9abd021ffa --- /dev/null +++ b/leo-inputs/src/types/boolean_type.rs @@ -0,0 +1,7 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_boolean))] +pub struct BooleanType {} diff --git a/leo-inputs/src/types/data_type.rs b/leo-inputs/src/types/data_type.rs new file mode 100644 index 0000000000..d675c59e6e --- /dev/null +++ b/leo-inputs/src/types/data_type.rs @@ -0,0 +1,26 @@ +use crate::{ + ast::Rule, + types::{BooleanType, FieldType, GroupType, IntegerType}, +}; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_data))] +pub enum DataType { + Integer(IntegerType), + Field(FieldType), + Group(GroupType), + Boolean(BooleanType), +} + +impl std::fmt::Display for DataType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + 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/field_type.rs b/leo-inputs/src/types/field_type.rs new file mode 100644 index 0000000000..d3751a2432 --- /dev/null +++ b/leo-inputs/src/types/field_type.rs @@ -0,0 +1,7 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_field))] +pub struct FieldType {} diff --git a/leo-inputs/src/types/group_type.rs b/leo-inputs/src/types/group_type.rs new file mode 100644 index 0000000000..c099ab778a --- /dev/null +++ b/leo-inputs/src/types/group_type.rs @@ -0,0 +1,7 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_group))] +pub struct GroupType {} diff --git a/leo-inputs/src/types/integer_type.rs b/leo-inputs/src/types/integer_type.rs new file mode 100644 index 0000000000..ed2b520d1c --- /dev/null +++ b/leo-inputs/src/types/integer_type.rs @@ -0,0 +1,45 @@ +use crate::ast::Rule; + +use pest_ast::FromPest; + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::type_integer))] +pub enum IntegerType { + U8Type(U8Type), + U16Type(U16Type), + U32Type(U32Type), + U64Type(U64Type), + U128Type(U128Type), +} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::type_u8))] +pub struct U8Type {} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::type_u16))] +pub struct U16Type {} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::type_u32))] +pub struct U32Type {} + +#[derive(Clone, Debug, FromPest, PartialEq, Eq)] +#[pest_ast(rule(Rule::type_u64))] +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/mod.rs b/leo-inputs/src/types/mod.rs new file mode 100644 index 0000000000..9db6353b8d --- /dev/null +++ b/leo-inputs/src/types/mod.rs @@ -0,0 +1,20 @@ +pub mod array_type; +pub use array_type::*; + +pub mod boolean_type; +pub use boolean_type::*; + +pub mod data_type; +pub use data_type::*; + +pub mod field_type; +pub use field_type::*; + +pub mod group_type; +pub use group_type::*; + +pub mod integer_type; +pub use integer_type::*; + +pub mod type_; +pub use type_::*; diff --git a/leo-inputs/src/types/type_.rs b/leo-inputs/src/types/type_.rs new file mode 100644 index 0000000000..a7a823ecab --- /dev/null +++ b/leo-inputs/src/types/type_.rs @@ -0,0 +1,20 @@ +use crate::{ast::Rule, types::*}; + +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::type_))] +pub enum Type<'ast> { + Basic(DataType), + Array(ArrayType<'ast>), +} + +impl<'ast> fmt::Display for Type<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Type::Basic(ref basic) => write!(f, "{}", basic), + Type::Array(ref array) => write!(f, "{}", array), + } + } +} diff --git a/leo-inputs/src/values/boolean_value.rs b/leo-inputs/src/values/boolean_value.rs new file mode 100644 index 0000000000..c16b9be740 --- /dev/null +++ b/leo-inputs/src/values/boolean_value.rs @@ -0,0 +1,20 @@ +use crate::ast::{span_into_string, Rule}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_boolean))] +pub struct BooleanValue<'ast> { + #[pest_ast(outer(with(span_into_string)))] + pub value: String, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for BooleanValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.value) + } +} diff --git a/leo-inputs/src/values/field_value.rs b/leo-inputs/src/values/field_value.rs new file mode 100644 index 0000000000..3c21e56cc8 --- /dev/null +++ b/leo-inputs/src/values/field_value.rs @@ -0,0 +1,20 @@ +use crate::{ast::Rule, types::FieldType, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_field))] +pub struct FieldValue<'ast> { + pub number: NumberValue<'ast>, + pub _type: FieldType, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for FieldValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.number) + } +} diff --git a/leo-inputs/src/values/group_value.rs b/leo-inputs/src/values/group_value.rs new file mode 100644 index 0000000000..865276658b --- /dev/null +++ b/leo-inputs/src/values/group_value.rs @@ -0,0 +1,35 @@ +use crate::{ast::Rule, types::GroupType, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_group))] +pub struct GroupValue<'ast> { + pub value: GroupTuple<'ast>, + pub _type: GroupType, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for GroupValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.value) + } +} + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::group_tuple))] +pub struct GroupTuple<'ast> { + pub x: NumberValue<'ast>, + pub y: NumberValue<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for GroupTuple<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } +} diff --git a/leo-inputs/src/values/integer_value.rs b/leo-inputs/src/values/integer_value.rs new file mode 100644 index 0000000000..141cb9aab6 --- /dev/null +++ b/leo-inputs/src/values/integer_value.rs @@ -0,0 +1,20 @@ +use crate::{ast::Rule, types::IntegerType, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_integer))] +pub struct IntegerValue<'ast> { + pub number: NumberValue<'ast>, + pub _type: IntegerType, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for IntegerValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.number) + } +} diff --git a/leo-inputs/src/values/mod.rs b/leo-inputs/src/values/mod.rs new file mode 100644 index 0000000000..87090f0ffa --- /dev/null +++ b/leo-inputs/src/values/mod.rs @@ -0,0 +1,20 @@ +pub mod boolean_value; +pub use boolean_value::*; + +pub mod field_value; +pub use field_value::*; + +pub mod group_value; +pub use group_value::*; + +pub mod integer_value; +pub use integer_value::*; + +pub mod number_implicit_value; +pub use number_implicit_value::*; + +pub mod number_value; +pub use number_value::*; + +pub mod value; +pub use value::*; diff --git a/leo-inputs/src/values/number_implicit_value.rs b/leo-inputs/src/values/number_implicit_value.rs new file mode 100644 index 0000000000..0aae2cc51e --- /dev/null +++ b/leo-inputs/src/values/number_implicit_value.rs @@ -0,0 +1,19 @@ +use crate::{ast::Rule, values::NumberValue}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_implicit))] +pub struct NumberImplicitValue<'ast> { + pub number: NumberValue<'ast>, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for NumberImplicitValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.number) + } +} diff --git a/leo-inputs/src/values/number_value.rs b/leo-inputs/src/values/number_value.rs new file mode 100644 index 0000000000..1054911286 --- /dev/null +++ b/leo-inputs/src/values/number_value.rs @@ -0,0 +1,20 @@ +use crate::ast::{span_into_string, Rule}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value_number))] +pub struct NumberValue<'ast> { + #[pest_ast(outer(with(span_into_string)))] + pub value: String, + #[pest_ast(outer())] + pub span: Span<'ast>, +} + +impl<'ast> fmt::Display for NumberValue<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.value) + } +} diff --git a/leo-inputs/src/values/value.rs b/leo-inputs/src/values/value.rs new file mode 100644 index 0000000000..6c3601fb26 --- /dev/null +++ b/leo-inputs/src/values/value.rs @@ -0,0 +1,42 @@ +use crate::{ + ast::Rule, + values::{BooleanValue, FieldValue, GroupValue, IntegerValue, NumberImplicitValue}, +}; + +use pest::Span; +use pest_ast::FromPest; +use std::fmt; + +#[derive(Clone, Debug, FromPest, PartialEq)] +#[pest_ast(rule(Rule::value))] +pub enum Value<'ast> { + Integer(IntegerValue<'ast>), + Field(FieldValue<'ast>), + Group(GroupValue<'ast>), + Boolean(BooleanValue<'ast>), + Implicit(NumberImplicitValue<'ast>), +} + +impl<'ast> Value<'ast> { + pub fn span(&self) -> &Span<'ast> { + match self { + Value::Integer(value) => &value.span, + Value::Field(value) => &value.span, + Value::Group(value) => &value.span, + Value::Boolean(value) => &value.span, + Value::Implicit(value) => &value.span, + } + } +} + +impl<'ast> fmt::Display for Value<'ast> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Value::Integer(ref value) => write!(f, "{}", value), + Value::Field(ref value) => write!(f, "{}", value), + Value::Group(ref value) => write!(f, "{}", value), + Value::Boolean(ref value) => write!(f, "{}", value), + Value::Implicit(ref value) => write!(f, "{}", value), + } + } +} diff --git a/leo/commands/init.rs b/leo/commands/init.rs index d1b79fc19f..2846940f59 100644 --- a/leo/commands/init.rs +++ b/leo/commands/init.rs @@ -6,6 +6,7 @@ use crate::{ files::{Gitignore, MainFile, Manifest}, }; +use crate::files::InputsFile; use clap::ArgMatches; use std::env::current_dir; @@ -65,6 +66,12 @@ impl CLI for InitCommand { // Create the inputs directory InputsDirectory::create(&path)?; + // Verify the inputs file does not exist + if !InputsFile::exists_at(&path) { + // Create the main file in the source directory + InputsFile::new(&package_name).write_to(&path)?; + } + // Verify the main file does not exist if !MainFile::exists_at(&path) { // Create the main file in the source directory diff --git a/leo/commands/prove.rs b/leo/commands/prove.rs index 64e3423db3..787d681884 100644 --- a/leo/commands/prove.rs +++ b/leo/commands/prove.rs @@ -6,11 +6,15 @@ use crate::{ files::{Manifest, ProofFile}, }; -use snarkos_algorithms::snark::{create_random_proof, Proof}; +use snarkos_algorithms::snark::{create_random_proof, PreparedVerifyingKey, Proof}; use snarkos_curves::bls12_377::Bls12_377; +use crate::{directories::INPUTS_DIRECTORY_NAME, files::INPUTS_FILE_NAME}; use clap::ArgMatches; +use leo_compiler::{compiler::Compiler, edwards_bls12::EdwardsGroupType}; +use leo_inputs::LeoInputsParser; use rand::thread_rng; +use snarkos_curves::edwards_bls12::Fq; use std::{convert::TryFrom, env::current_dir, time::Instant}; #[derive(Debug)] @@ -18,7 +22,11 @@ pub struct ProveCommand; impl CLI for ProveCommand { type Options = (); - type Output = Proof; + type Output = ( + Compiler, + Proof, + PreparedVerifyingKey, + ); const ABOUT: AboutType = "Run the program and produce a proof"; const ARGUMENTS: &'static [ArgumentType] = &[]; @@ -34,17 +42,26 @@ impl CLI for ProveCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (program, parameters, _) = SetupCommand::output(options)?; + let (mut program, parameters, prepared_verifying_key) = SetupCommand::output(options)?; // Get the package name let path = current_dir()?; let package_name = Manifest::try_from(&path)?.get_package_name(); + // Construct the path to the inputs file in the inputs directory + let mut inputs_file_path = path.clone(); + inputs_file_path.push(INPUTS_DIRECTORY_NAME); + inputs_file_path.push(INPUTS_FILE_NAME); + + // Fetch program inputs here + let inputs_file_string = LeoInputsParser::load_file(&inputs_file_path)?; + program.parse_inputs(&inputs_file_path, &inputs_file_string)?; + // Start the timer let start = Instant::now(); let rng = &mut thread_rng(); - let program_proof = create_random_proof(program, ¶meters, rng).unwrap(); + let program_proof = create_random_proof(program.clone(), ¶meters, rng)?; log::info!("Prover completed in {:?} milliseconds", start.elapsed().as_millis()); @@ -55,6 +72,6 @@ impl CLI for ProveCommand { log::info!("Completed program proving"); - Ok(program_proof) + Ok((program, program_proof, prepared_verifying_key)) } } diff --git a/leo/commands/run.rs b/leo/commands/run.rs index a7004ee18c..c0f42b2191 100644 --- a/leo/commands/run.rs +++ b/leo/commands/run.rs @@ -1,11 +1,7 @@ -use crate::{ - cli::*, - cli_types::*, - commands::{ProveCommand, SetupCommand}, - errors::CLIError, -}; +use crate::{cli::*, cli_types::*, commands::ProveCommand, errors::CLIError}; use snarkos_algorithms::snark::verify_proof; +use snarkos_curves::bls12_377::Bls12_377; use clap::ArgMatches; use std::time::{Duration, Instant}; @@ -31,16 +27,16 @@ impl CLI for RunCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result<(), CLIError> { - let (_program, _parameters, prepared_verifying_key) = SetupCommand::output(options)?; - let proof = ProveCommand::output(options)?; + let (program, proof, prepared_verifying_key) = ProveCommand::output(options)?; let mut verifying = Duration::new(0, 0); - // let _inputs: Vec<_> = [1u32; 1].to_vec(); + // fetch public inputs + let inputs: Vec<_> = program.get_public_inputs::().unwrap(); let start = Instant::now(); - let is_success = verify_proof(&prepared_verifying_key, &proof, &[]).unwrap(); + let is_success = verify_proof(&prepared_verifying_key, &proof, &inputs).unwrap(); verifying += start.elapsed(); diff --git a/leo/errors/cli.rs b/leo/errors/cli.rs index 007d2cffaf..53599b83cc 100644 --- a/leo/errors/cli.rs +++ b/leo/errors/cli.rs @@ -57,6 +57,12 @@ impl From for CLIError { } } +impl From for CLIError { + fn from(error: leo_inputs::errors::InputParserError) -> Self { + CLIError::Crate("leo_inputs", format!("{}", error)) + } +} + impl From for CLIError { fn from(error: snarkos_errors::gadgets::SynthesisError) -> Self { CLIError::Crate("snarkos_errors", format!("{}", error)) diff --git a/leo/files/inputs.rs b/leo/files/inputs.rs new file mode 100644 index 0000000000..138f671a82 --- /dev/null +++ b/leo/files/inputs.rs @@ -0,0 +1,56 @@ +//! The `inputs.leo` file. + +use crate::{directories::inputs::INPUTS_DIRECTORY_NAME, errors::MainFileError}; + +use serde::Deserialize; +use std::{fs::File, io::Write, path::PathBuf}; + +pub static INPUTS_FILE_NAME: &str = "inputs.leo"; + +#[derive(Deserialize)] +pub struct InputsFile { + pub package_name: String, +} + +impl InputsFile { + pub fn new(package_name: &str) -> Self { + Self { + package_name: package_name.to_string(), + } + } + + pub fn exists_at(path: &PathBuf) -> bool { + let mut path = path.to_owned(); + if path.is_dir() { + if !path.ends_with(INPUTS_DIRECTORY_NAME) { + path.push(PathBuf::from(INPUTS_DIRECTORY_NAME)); + } + path.push(PathBuf::from(INPUTS_FILE_NAME)); + } + path.exists() + } + + pub fn write_to(self, path: &PathBuf) -> Result<(), MainFileError> { + let mut path = path.to_owned(); + if path.is_dir() { + if !path.ends_with(INPUTS_DIRECTORY_NAME) { + path.push(PathBuf::from(INPUTS_DIRECTORY_NAME)); + } + path.push(PathBuf::from(INPUTS_FILE_NAME)); + } + + let mut file = File::create(&path)?; + Ok(file.write_all(self.template().as_bytes())?) + } + + fn template(&self) -> String { + format!( + r#"// The inputs for {}/src/main.leo +[main] +a: private u32 = 1; +b: public u32 = 2; +"#, + self.package_name + ) + } +} diff --git a/leo/files/main.rs b/leo/files/main.rs index 9411fff570..955999d047 100644 --- a/leo/files/main.rs +++ b/leo/files/main.rs @@ -46,9 +46,9 @@ impl MainFile { fn template(&self) -> String { format!( r#"// The '{}' main function. -function main() -> u32 {{ - let a: u32 = 1 + 1; - return a +function main(a: private u32, b: public u32) -> u32 {{ + let c: u32 = a + b; + return c }} "#, self.package_name diff --git a/leo/files/mod.rs b/leo/files/mod.rs index 556bb337ef..6ef50f807b 100644 --- a/leo/files/mod.rs +++ b/leo/files/mod.rs @@ -1,6 +1,9 @@ pub mod checksum; pub use self::checksum::*; +pub mod inputs; +pub use self::inputs::*; + pub mod gitignore; pub use self::gitignore::*; diff --git a/types/Cargo.toml b/types/Cargo.toml index 4bab67b62d..ed673f1d3d 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] leo-ast = { path = "../ast", version = "0.1.0" } +leo-inputs = { path = "../leo-inputs", version = "0.1.0" } snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0" } snarkos-models = { path = "../../snarkOS/models", version = "0.8.0" } 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/input_value.rs b/types/src/input_value.rs deleted file mode 100644 index 3e7cf72732..0000000000 --- a/types/src/input_value.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::fmt; - -#[derive(Clone, PartialEq, Eq)] -pub enum InputValue { - Integer(u128), - Field(String), - Group(String), - Boolean(bool), - Array(Vec), -} - -impl fmt::Display for InputValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - InputValue::Integer(ref integer) => write!(f, "{}", integer), - InputValue::Field(ref field) => write!(f, "{}", field), - InputValue::Group(ref group) => write!(f, "{}", group), - InputValue::Boolean(ref bool) => write!(f, "{}", bool), - InputValue::Array(ref array) => { - write!(f, "[")?; - for (i, e) in array.iter().enumerate() { - write!(f, "{}", e)?; - if i < array.len() - 1 { - write!(f, ", ")?; - } - } - write!(f, "]") - } - } - } -} diff --git a/types/src/inputs/input_fields.rs b/types/src/inputs/input_fields.rs new file mode 100644 index 0000000000..30f3324b55 --- /dev/null +++ b/types/src/inputs/input_fields.rs @@ -0,0 +1,70 @@ +use crate::InputValue; +use leo_inputs::{types::IntegerType, InputParserError}; + +use snarkos_models::curves::{Field, PairingEngine}; +use std::str::FromStr; + +pub struct InputFields(pub Vec); + +impl InputFields { + pub(crate) fn from_boolean(boolean: &bool) -> Self { + if *boolean { + Self(vec![E::Fr::one()]) + } else { + Self(vec![E::Fr::zero()]) + } + } + + pub(crate) fn from_integer(type_: &IntegerType, integer: &u128) -> Self { + let bits: usize = match type_ { + IntegerType::U8Type(_) => 8, + IntegerType::U16Type(_) => 16, + IntegerType::U32Type(_) => 32, + IntegerType::U64Type(_) => 64, + IntegerType::U128Type(_) => 128, + }; + let mut fields = vec![]; + + for i in 0..bits { + let boolean = (integer.to_le() >> i) & 1 == 1; + let mut boolean_fields = InputFields::::from_boolean(&boolean); + + fields.append(&mut boolean_fields.0); + } + + Self(fields) + } + + pub(crate) fn from_field(field: &str) -> Result { + let field = E::Fr::from_str(field).map_err(|_| InputParserError::ParseFieldError(field.to_string()))?; + + Ok(Self(vec![field])) + } + + pub(crate) fn from_group(group: &str) -> Result { + let s = group.trim(); + let mut fields = vec![]; + + for substr in s.split(|c| c == '(' || c == ')' || c == ',' || c == ' ') { + if !substr.is_empty() { + let mut input_fields = InputFields::::from_field(&substr)?; + + fields.append(&mut input_fields.0); + } + } + + Ok(Self(fields)) + } + + pub(crate) fn from_array(array: &Vec) -> Result { + let mut fields = vec![]; + + for input in array.iter() { + let mut input_fields = input.to_input_fields::()?; + + fields.append(&mut input_fields.0); + } + + Ok(Self(fields)) + } +} diff --git a/types/src/inputs/input_value.rs b/types/src/inputs/input_value.rs new file mode 100644 index 0000000000..dbc16ed44f --- /dev/null +++ b/types/src/inputs/input_value.rs @@ -0,0 +1,183 @@ +use crate::InputFields; +use leo_inputs::{ + errors::InputParserError, + expressions::{ArrayInitializerExpression, ArrayInlineExpression, Expression}, + types::{ArrayType, DataType, IntegerType, Type}, + values::{BooleanValue, FieldValue, GroupValue, NumberImplicitValue, NumberValue, Value}, +}; + +use snarkos_models::curves::PairingEngine; +use std::fmt; + +#[derive(Clone, PartialEq, Eq)] +pub enum InputValue { + Integer(IntegerType, u128), + Field(String), + Group(String), + Boolean(bool), + Array(Vec), +} + +impl<'ast> InputValue { + fn from_boolean(boolean: BooleanValue<'ast>) -> Result { + let boolean = boolean.value.parse::()?; + Ok(InputValue::Boolean(boolean)) + } + + fn from_number(integer_type: IntegerType, number: NumberValue<'ast>) -> Result { + let integer = number.value.parse::()?; + Ok(InputValue::Integer(integer_type, integer)) + } + + fn from_group(group: GroupValue<'ast>) -> Self { + InputValue::Group(group.to_string()) + } + + fn from_field(field: FieldValue<'ast>) -> Self { + InputValue::Field(field.number.value) + } + + fn from_implicit(data_type: DataType, implicit: NumberImplicitValue<'ast>) -> Result { + match data_type { + DataType::Boolean(_) => Err(InputParserError::IncompatibleTypes( + "bool".to_string(), + "implicit number".to_string(), + )), + DataType::Integer(integer_type) => InputValue::from_number(integer_type, implicit.number), + DataType::Group(_) => Ok(InputValue::Group(implicit.number.value)), + DataType::Field(_) => Ok(InputValue::Field(implicit.number.value)), + } + } + + fn from_value(data_type: DataType, value: Value<'ast>) -> Result { + match (data_type, value) { + (DataType::Boolean(_), Value::Boolean(boolean)) => InputValue::from_boolean(boolean), + (DataType::Integer(integer_type), Value::Integer(integer)) => { + InputValue::from_number(integer_type, integer.number) + } + (DataType::Group(_), Value::Group(group)) => Ok(InputValue::from_group(group)), + (DataType::Field(_), Value::Field(field)) => Ok(InputValue::from_field(field)), + (data_type, Value::Implicit(implicit)) => InputValue::from_implicit(data_type, implicit), + (data_type, value) => Err(InputParserError::IncompatibleTypes( + data_type.to_string(), + value.to_string(), + )), + } + } + + pub(crate) fn from_expression(type_: Type<'ast>, expression: Expression<'ast>) -> Result { + match (type_, expression) { + (Type::Basic(data_type), Expression::Value(value)) => InputValue::from_value(data_type, value), + (Type::Array(array_type), Expression::ArrayInline(inline)) => { + InputValue::from_array_inline(array_type, inline) + } + (Type::Array(array_type), Expression::ArrayInitializer(initializer)) => { + InputValue::from_array_initializer(array_type, initializer) + } + (type_, value) => Err(InputParserError::IncompatibleTypes( + type_.to_string(), + value.to_string(), + )), + } + } + + pub(crate) fn from_array_inline( + mut array_type: ArrayType, + inline: ArrayInlineExpression, + ) -> Result { + match array_type.next_dimension() { + Some(number) => { + let outer_dimension = number.value.parse::()?; + + if outer_dimension != inline.expressions.len() { + return Err(InputParserError::InvalidArrayLength( + outer_dimension, + inline.expressions.len(), + )); + } + } + None => return Err(InputParserError::UndefinedArrayDimension), + } + + let inner_array_type = if array_type.dimensions.len() == 0 { + // this is a single array + Type::Basic(array_type._type) + } else { + Type::Array(array_type) + }; + + let mut values = vec![]; + for expression in inline.expressions.into_iter() { + let value = InputValue::from_expression(inner_array_type.clone(), expression)?; + + values.push(value) + } + + Ok(InputValue::Array(values)) + } + + pub(crate) fn from_array_initializer( + mut array_type: ArrayType, + initializer: ArrayInitializerExpression, + ) -> Result { + let initializer_count = initializer.count.value.parse::()?; + + match array_type.next_dimension() { + Some(number) => { + let outer_dimension = number.value.parse::()?; + + if outer_dimension != initializer_count { + return Err(InputParserError::InvalidArrayLength(outer_dimension, initializer_count)); + } + } + None => return Err(InputParserError::UndefinedArrayDimension), + } + + let inner_array_type = if array_type.dimensions.len() == 0 { + // this is a single array + Type::Basic(array_type._type) + } else { + Type::Array(array_type) + }; + + let mut values = vec![]; + for _ in 0..initializer_count { + let value = InputValue::from_expression(inner_array_type.clone(), *initializer.expression.clone())?; + + values.push(value) + } + + Ok(InputValue::Array(values)) + } + + pub(crate) fn to_input_fields(&self) -> Result, InputParserError> { + match self { + InputValue::Boolean(boolean) => Ok(InputFields::from_boolean(boolean)), + InputValue::Integer(type_, number) => Ok(InputFields::from_integer(type_, number)), + InputValue::Group(group) => InputFields::from_group(group), + InputValue::Field(field) => InputFields::from_field(field), + InputValue::Array(array) => InputFields::from_array(array), + } + } +} + +impl fmt::Display for InputValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + InputValue::Boolean(ref boolean) => write!(f, "{}", boolean), + InputValue::Integer(ref type_, ref number) => write!(f, "{}{:?}", number, type_), + InputValue::Group(ref group) => write!(f, "{}", group), + InputValue::Field(ref field) => write!(f, "{}", field), + InputValue::Array(ref array) => { + write!(f, "[")?; + for (i, e) in array.iter().enumerate() { + write!(f, "{}", e)?; + if i < array.len() - 1 { + write!(f, ", ")?; + } + } + write!(f, "]") + } + } + } +} diff --git a/types/src/inputs/inputs.rs b/types/src/inputs/inputs.rs new file mode 100644 index 0000000000..d5b2b8b701 --- /dev/null +++ b/types/src/inputs/inputs.rs @@ -0,0 +1,92 @@ +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)] +pub struct Inputs { + program_inputs: Vec>, + public: Vec, +} + +impl Inputs { + pub fn new() -> Self { + Self { + program_inputs: vec![], + public: vec![], + } + } + + pub fn get_inputs(&self) -> Vec> { + self.program_inputs.clone() + } + + pub fn set_inputs(&mut self, inputs: Vec>) { + self.program_inputs = inputs; + } + + pub fn set_inputs_size(&mut self, size: usize) { + self.program_inputs = vec![None; size]; + } + + 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() { + 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 {}))); + + // 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())), + } + } + } + } + + Ok(Self { + program_inputs: private, + public, + }) + } + + pub fn get_public_inputs(&self) -> Result, InputParserError> { + let mut input_vec = vec![]; + + for input in self.public.iter() { + // get fields + let mut input_fields = input.to_input_fields::()?; + + // push fields to input_vec + input_vec.append(&mut input_fields.0) + } + + Ok(input_vec) + } +} diff --git a/types/src/inputs/mod.rs b/types/src/inputs/mod.rs new file mode 100644 index 0000000000..085568ef93 --- /dev/null +++ b/types/src/inputs/mod.rs @@ -0,0 +1,8 @@ +pub mod inputs; +pub use inputs::*; + +pub mod input_fields; +pub use input_fields::*; + +pub mod input_value; +pub use input_value::*; diff --git a/types/src/integer.rs b/types/src/integer.rs index a6d056f740..eff0b91dfe 100644 --- a/types/src/integer.rs +++ b/types/src/integer.rs @@ -87,7 +87,7 @@ impl Integer { // Check that the input value is the correct type let integer_option = match integer_value { Some(input) => { - if let InputValue::Integer(integer) = input { + if let InputValue::Integer(_type_, integer) = input { Some(integer) } else { return Err(IntegerError::InvalidInteger(input.to_string())); diff --git a/types/src/lib.rs b/types/src/lib.rs index 431c25c8d2..d398829f59 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -19,8 +19,8 @@ pub use functions::*; pub mod imports; pub use imports::*; -pub mod input_value; -pub use input_value::*; +pub mod inputs; +pub use inputs::*; pub mod integer; pub use integer::*; 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(),