From 18768bbec72e1d256c6e584db4fd1126c5ee0b47 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 25 Apr 2020 01:47:10 -0700 Subject: [PATCH] Adds command --- Cargo.lock | 3 + Cargo.toml | 4 + leo/commands/init.rs | 3 +- leo/commands/mod.rs | 3 + leo/commands/run.rs | 135 ++++++++++++++++++++++++++++++++ leo/compiler.rs | 50 ++++++++++++ leo/directories/mod.rs | 3 + leo/directories/outputs.rs | 34 ++++++++ leo/errors/cli.rs | 20 ++++- leo/errors/commands/mod.rs | 3 + leo/errors/commands/run.rs | 33 ++++++++ leo/errors/directory/mod.rs | 3 + leo/errors/directory/outputs.rs | 30 +++++++ leo/files/main.rs | 6 +- leo/lib.rs | 2 + leo/logger.rs | 45 +++++++++++ leo/main.rs | 10 ++- leo/simple.leo | 4 - leo/simple_import.leo | 4 - 19 files changed, 378 insertions(+), 17 deletions(-) create mode 100644 leo/commands/run.rs create mode 100644 leo/compiler.rs create mode 100644 leo/directories/outputs.rs create mode 100644 leo/errors/commands/run.rs create mode 100644 leo/errors/directory/outputs.rs create mode 100644 leo/logger.rs delete mode 100644 leo/simple.leo delete mode 100644 leo/simple_import.leo diff --git a/Cargo.lock b/Cargo.lock index 123be2b395..9913b156ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,8 +613,11 @@ version = "0.1.0" dependencies = [ "clap", "colored", + "env_logger", "failure", "from-pest", + "leo-program", + "log", "rand 0.7.3", "rand_core 0.5.1", "serde", diff --git a/Cargo.toml b/Cargo.toml index 4d7760d007..7afe57dd86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ path = "leo/main.rs" members = [ "benchmark", "program" ] [dependencies] +leo-program = { path = "./program", version = "0.1.0" } + 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" } @@ -24,8 +26,10 @@ snarkos-models = { path = "../snarkOS/models", version = "0.8.0" } clap = { version = "2.33.0" } colored = { version = "1.9" } +env_logger = { version = "0.7" } failure = { version = "0.1.5" } from-pest = { version = "0.3.1" } +log = { version = "0.4" } rand = { version = "0.7" } rand_core = { version = "0.5.1" } serde = { version = "1.0", features = ["derive"] } diff --git a/leo/commands/init.rs b/leo/commands/init.rs index 7050757856..6089c7007a 100644 --- a/leo/commands/init.rs +++ b/leo/commands/init.rs @@ -56,8 +56,9 @@ impl CLI for InitCommand { // Create the inputs directory InputsDirectory::create(&path)?; - // Create the main file in the source directory + // Verify the main file does not exist if !MainFile::exists_at(&path) { + // Create the main file in the source directory MainFile::new(&package_name).write_to(&path)?; } diff --git a/leo/commands/mod.rs b/leo/commands/mod.rs index 2d5b8771e7..b8ba748f1e 100644 --- a/leo/commands/mod.rs +++ b/leo/commands/mod.rs @@ -1,2 +1,5 @@ pub mod init; pub use self::init::*; + +pub mod run; +pub use self::run::*; diff --git a/leo/commands/run.rs b/leo/commands/run.rs new file mode 100644 index 0000000000..6d0e205acc --- /dev/null +++ b/leo/commands/run.rs @@ -0,0 +1,135 @@ +use crate::{cli::*, cli_types::*}; +use crate::compiler::Compiler; +use crate::directories::{OutputsDirectory, source::SOURCE_DIRECTORY_NAME}; +use crate::errors::{CLIError, RunError}; +use crate::files::{MainFile, MAIN_FILE_NAME}; +use crate::manifest::Manifest; + +use snarkos_curves::bls12_377::Fr; + +use clap::ArgMatches; +use std::convert::TryFrom; +use std::env::current_dir; +use std::path::PathBuf; + +#[derive(Debug)] +pub struct RunCommand; + +impl CLI for RunCommand { + type Options = (); + + const NAME: NameType = "run"; + const ABOUT: AboutType = "Run a program with inputs (include -h for more options)"; + const FLAGS: &'static [FlagType] = &[]; + const OPTIONS: &'static [OptionType] = &[]; + const SUBCOMMANDS: &'static [SubCommandType] = &[]; + + #[cfg_attr(tarpaulin, skip)] + fn parse(_arguments: &ArgMatches) -> Result { + Ok(()) + } + + #[cfg_attr(tarpaulin, skip)] + fn output(_options: Self::Options) -> Result<(), CLIError> { + let path = current_dir()?; + let _manifest = Manifest::try_from(&path)?; + + // Sanitize the package path to the root directory + let mut package_path = path.clone(); + if package_path.is_file() { + package_path.pop(); + } + + // Verify the main file exists + if !MainFile::exists_at(&package_path) { + return Err(RunError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into()); + } + + // Create the outputs directory + OutputsDirectory::create(&package_path)?; + + // Construct the path to the main file in the source directory + let mut main_file_path = package_path.clone(); + main_file_path.push(SOURCE_DIRECTORY_NAME); + main_file_path.push(MAIN_FILE_NAME); + + log::debug!("Compiling program located in {:?}", main_file_path); + + fn run(main_file_path: PathBuf) { + + use snarkos_algorithms::snark::{create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof}; + use snarkos_curves::bls12_377::Bls12_377; + + use rand::thread_rng; + use std::time::{Duration, Instant}; + + let mut setup = Duration::new(0, 0); + let mut proving = Duration::new(0, 0); + let mut verifying = Duration::new(0, 0); + + let rng = &mut thread_rng(); + + let start = Instant::now(); + + let params = { + let circuit = Compiler::::init(main_file_path.clone()); + generate_random_parameters::(circuit, rng).unwrap() + }; + + let prepared_verifying_key = prepare_verifying_key::(¶ms.vk); + + setup += start.elapsed(); + + let start = Instant::now(); + let proof = { + let circuit = Compiler::::init(main_file_path); + create_random_proof(circuit, ¶ms, rng).unwrap() + }; + + proving += start.elapsed(); + + // let _inputs: Vec<_> = [1u32; 1].to_vec(); + + let start = Instant::now(); + + let is_success = verify_proof(&prepared_verifying_key, &proof, &[]).unwrap(); + + verifying += start.elapsed(); + + println!(" "); + println!(" Setup time : {:?} milliseconds", setup.as_millis()); + println!(" Prover time : {:?} milliseconds", proving.as_millis()); + println!( + " Verifier time : {:?} milliseconds", + verifying.as_millis() + ); + println!(" Verifier output : {}", is_success); + println!(" "); + } + + run(main_file_path); + + // let source_files = SourceDirectory::files(&package_path)?; + // BuildDirectory::create(&circuit_path).map_err(Error::BuildDirectory)?; + // DataDirectory::create(&circuit_path).map_err(Error::DataDirectory)?; + + // Compiler::build( + // self.verbosity, + // &self.witness, + // &self.public_data, + // &self.circuit, + // &source_file_paths, + // ) + // .map_err(Error::Compiler)?; + // + // VirtualMachine::run( + // self.verbosity, + // &self.circuit, + // &self.witness, + // &self.public_data, + // ) + // .map_err(Error::VirtualMachine)?; + + Ok(()) + } +} diff --git a/leo/compiler.rs b/leo/compiler.rs new file mode 100644 index 0000000000..a8d1c2baab --- /dev/null +++ b/leo/compiler.rs @@ -0,0 +1,50 @@ +use leo_program::{self, ast}; + +use snarkos_errors::gadgets::SynthesisError; +use snarkos_models::{ + curves::{Field, PrimeField}, + gadgets::r1cs::{ConstraintSynthesizer, ConstraintSystem}, +}; + +use from_pest::FromPest; +use std::{ + fs, + marker::PhantomData, + path::PathBuf, +}; + +pub struct Compiler { + main_file_path: PathBuf, + _engine: PhantomData, +} + +impl Compiler { + pub fn init(main_file_path: PathBuf) -> Self { + Self { main_file_path, _engine: PhantomData } + } +} + +impl ConstraintSynthesizer for Compiler { + fn generate_constraints>( + self, + cs: &mut CS, + ) -> Result<(), SynthesisError> { + // Read in the main file as string + let unparsed_file = fs::read_to_string(&self.main_file_path).expect("cannot read file"); + + // Parse the file using leo.pest + let mut file = ast::parse(&unparsed_file).expect("unsuccessful parse"); + + // Build the abstract syntax tree + let syntax_tree = ast::File::from_pest(&mut file).expect("infallible"); + // println!("{:#?}", syntax_tree); + + let program = leo_program::Program::<'_, F>::from(syntax_tree); + println!(" compiled: {:#?}", program); + + let program = program.name("simple".into()); + leo_program::ResolvedProgram::generate_constraints(cs, program); + + Ok(()) + } +} \ No newline at end of file diff --git a/leo/directories/mod.rs b/leo/directories/mod.rs index cbc68327b5..31f99c6104 100644 --- a/leo/directories/mod.rs +++ b/leo/directories/mod.rs @@ -1,5 +1,8 @@ pub mod inputs; pub use self::inputs::*; +pub mod outputs; +pub use self::outputs::*; + pub mod source; pub use self::source::*; diff --git a/leo/directories/outputs.rs b/leo/directories/outputs.rs new file mode 100644 index 0000000000..eaf1d4928d --- /dev/null +++ b/leo/directories/outputs.rs @@ -0,0 +1,34 @@ +use crate::errors::OutputsDirectoryError; + +use std::fs; +use std::path::PathBuf; + +pub(crate) static OUTPUTS_DIRECTORY_NAME: &str = "outputs/"; + +pub struct OutputsDirectory; + +impl OutputsDirectory { + /// Creates a directory at the provided path with the default directory name. + pub fn create(path: &PathBuf) -> Result<(), OutputsDirectoryError> { + let mut path = path.to_owned(); + if path.is_dir() && !path.ends_with(OUTPUTS_DIRECTORY_NAME) { + path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME)); + } + + fs::create_dir_all(&path).map_err(OutputsDirectoryError::Creating) + } + + /// Removes the directory at the provided path. + pub fn remove(path: &PathBuf) -> Result<(), OutputsDirectoryError> { + let mut path = path.to_owned(); + if path.is_dir() && !path.ends_with(OUTPUTS_DIRECTORY_NAME) { + path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME)); + } + + if path.exists() { + fs::remove_dir_all(&path).map_err(OutputsDirectoryError::Removing)?; + } + + Ok(()) + } +} diff --git a/leo/errors/cli.rs b/leo/errors/cli.rs index 9dccf1ffdd..b37af25e75 100644 --- a/leo/errors/cli.rs +++ b/leo/errors/cli.rs @@ -1,4 +1,4 @@ -use crate::errors::{InitError, InputsDirectoryError, MainFileError, ManifestError, SourceDirectoryError}; +use crate::errors::{InitError, InputsDirectoryError, MainFileError, ManifestError, OutputsDirectoryError, RunError, SourceDirectoryError}; #[derive(Debug, Fail)] pub enum CLIError { @@ -18,6 +18,12 @@ pub enum CLIError { #[fail(display = "{}", _0)] ManifestError(ManifestError), + #[fail(display = "{}", _0)] + OutputsDirectoryError(OutputsDirectoryError), + + #[fail(display = "{}", _0)] + RunError(RunError), + #[fail(display = "{}", _0)] SourceDirectoryError(SourceDirectoryError), @@ -47,6 +53,18 @@ impl From for CLIError { } } +impl From for CLIError { + fn from(error: OutputsDirectoryError) -> Self { + CLIError::OutputsDirectoryError(error) + } +} + +impl From for CLIError { + fn from(error: RunError) -> Self { + CLIError::RunError(error) + } +} + impl From for CLIError { fn from(error: SourceDirectoryError) -> Self { CLIError::SourceDirectoryError(error) diff --git a/leo/errors/commands/mod.rs b/leo/errors/commands/mod.rs index 2d5b8771e7..b8ba748f1e 100644 --- a/leo/errors/commands/mod.rs +++ b/leo/errors/commands/mod.rs @@ -1,2 +1,5 @@ pub mod init; pub use self::init::*; + +pub mod run; +pub use self::run::*; diff --git a/leo/errors/commands/run.rs b/leo/errors/commands/run.rs new file mode 100644 index 0000000000..7d2c8e8696 --- /dev/null +++ b/leo/errors/commands/run.rs @@ -0,0 +1,33 @@ +use crate::errors::ManifestError; + +use std::ffi::OsString; +use std::io; + +#[derive(Debug, Fail)] +pub enum RunError { + + #[fail(display = "root directory {:?} creating: {}", _0, _1)] + CreatingRootDirectory(OsString, io::Error), + + #[fail(display = "directory {:?} does not exist", _0)] + DirectoryDoesNotExist(OsString), + + #[fail(display = "main file {:?} does not exist", _0)] + MainFileDoesNotExist(OsString), + + #[fail(display = "{}", _0)] + ManifestError(ManifestError), + + #[fail(display = "package at path {:?} already exists", _0)] + PackageAlreadyExists(OsString), + + #[fail(display = "package name is missing - {:?}", _0)] + ProjectNameInvalid(OsString), + +} + +impl From for RunError { + fn from(error: ManifestError) -> Self { + RunError::ManifestError(error) + } +} diff --git a/leo/errors/directory/mod.rs b/leo/errors/directory/mod.rs index cbc68327b5..31f99c6104 100644 --- a/leo/errors/directory/mod.rs +++ b/leo/errors/directory/mod.rs @@ -1,5 +1,8 @@ pub mod inputs; pub use self::inputs::*; +pub mod outputs; +pub use self::outputs::*; + pub mod source; pub use self::source::*; diff --git a/leo/errors/directory/outputs.rs b/leo/errors/directory/outputs.rs new file mode 100644 index 0000000000..87c61db1f0 --- /dev/null +++ b/leo/errors/directory/outputs.rs @@ -0,0 +1,30 @@ +use std::{ffi::OsString, fs::FileType, io}; + +#[derive(Debug, Fail)] +pub enum OutputsDirectoryError { + + #[fail(display = "creating: {}", _0)] + Creating(io::Error), + + #[fail(display = "file entry getting: {}", _0)] + GettingFileEntry(io::Error), + + #[fail(display = "file {:?} extension getting", _0)] + GettingFileExtension(OsString), + + #[fail(display = "file {:?} type getting: {}", _0, _1)] + GettingFileType(OsString, io::Error), + + #[fail(display = "invalid file {:?} extension: {:?}", _0, _1)] + InvalidFileExtension(OsString, OsString), + + #[fail(display = "invalid file {:?} type: {:?}", _0, _1)] + InvalidFileType(OsString, FileType), + + #[fail(display = "reading: {}", _0)] + Reading(io::Error), + + #[fail(display = "removing: {}", _0)] + Removing(io::Error), + +} diff --git a/leo/files/main.rs b/leo/files/main.rs index 0cf73879de..8862d66b51 100644 --- a/leo/files/main.rs +++ b/leo/files/main.rs @@ -8,7 +8,7 @@ use std::fs::File; use std::io::Write; use std::path::PathBuf; -pub static FILE_NAME_DEFAULT: &str = "main.leo"; +pub static MAIN_FILE_NAME: &str = "main.leo"; #[derive(Deserialize)] pub struct MainFile { @@ -26,7 +26,7 @@ impl MainFile { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(FILE_NAME_DEFAULT)); + path.push(PathBuf::from(MAIN_FILE_NAME)); } path.exists() } @@ -37,7 +37,7 @@ impl MainFile { if !path.ends_with(SOURCE_DIRECTORY_NAME) { path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); } - path.push(PathBuf::from(FILE_NAME_DEFAULT)); + path.push(PathBuf::from(MAIN_FILE_NAME)); } let mut file = File::create(&path)?; diff --git a/leo/lib.rs b/leo/lib.rs index 9342eecad8..84a0ef6202 100644 --- a/leo/lib.rs +++ b/leo/lib.rs @@ -5,7 +5,9 @@ extern crate failure; pub mod cli; pub mod cli_types; pub mod commands; +pub mod compiler; pub mod directories; pub mod errors; pub mod files; +pub mod logger; pub mod manifest; diff --git a/leo/logger.rs b/leo/logger.rs new file mode 100644 index 0000000000..e2ac5c3175 --- /dev/null +++ b/leo/logger.rs @@ -0,0 +1,45 @@ +use colored::Colorize; +use std::io::Write; + +const LEVEL_NAME_LENGTH: usize = 10; + +#[allow(dead_code)] +fn level_string(level: log::Level) -> colored::ColoredString { + match level { + log::Level::Error => "ERROR".bold().red(), + log::Level::Warn => "WARN".bold().yellow(), + log::Level::Info => "INFO".bold().blue(), + log::Level::Debug => "DEBUG".bold().magenta(), + log::Level::Trace => "TRACE".bold(), + } +} + +/// Initialize logger with custom format and verbosity. +/// +/// # Arguments +/// +/// * `verbosity` - Verbosity level. 0 for `Warn`, 1 for `Info`, 2 for `Debug`, more for `Trace` +pub fn init_logger(app_name: &'static str, verbosity: usize) { + env_logger::builder() + .filter_level(match verbosity { + 0 => log::LevelFilter::Warn, + 1 => log::LevelFilter::Info, + 2 => log::LevelFilter::Debug, + _ => log::LevelFilter::Trace, + }) + .format(move |buf, record| { + let mut padding = String::from("\n"); + for _ in 0..(app_name.len() + LEVEL_NAME_LENGTH + 4) { + padding.push(' '); + } + + writeln!( + buf, + "[{:>5} {:>5}] {}", + level_string(record.level()), + app_name, + record.args().to_string().replace("\n", &padding) + ) + }) + .init(); +} diff --git a/leo/main.rs b/leo/main.rs index bf3e8110d2..615de0adde 100644 --- a/leo/main.rs +++ b/leo/main.rs @@ -110,13 +110,15 @@ // // // } -use leo::{cli::*, commands::*}; +use leo::{cli::*, commands::*, logger}; use leo::errors::CLIError; use clap::{App, AppSettings}; #[cfg_attr(tarpaulin, skip)] fn main() -> Result<(), CLIError> { + logger::init_logger("leo", 3); + let arguments = App::new("leo") .version("v0.1.0") .about("Leo compiler and package manager") @@ -129,15 +131,15 @@ fn main() -> Result<(), CLIError> { ]) .subcommands(vec![ InitCommand::new(), + RunCommand::new(), ]) .set_term_width(0) .get_matches(); match arguments.subcommand() { - ("init", Some(arguments)) => { - InitCommand::output(InitCommand::parse(arguments)?) - }, + ("init", Some(arguments)) => InitCommand::output(InitCommand::parse(arguments)?), + ("run", Some(arguments)) => RunCommand::output(RunCommand::parse(arguments)?), _ => unreachable!(), } } \ No newline at end of file diff --git a/leo/simple.leo b/leo/simple.leo deleted file mode 100644 index 847c630eb2..0000000000 --- a/leo/simple.leo +++ /dev/null @@ -1,4 +0,0 @@ -function main() -> (u32) { - a = 1 + 1 - return a -} \ No newline at end of file diff --git a/leo/simple_import.leo b/leo/simple_import.leo deleted file mode 100644 index 667fa6871c..0000000000 --- a/leo/simple_import.leo +++ /dev/null @@ -1,4 +0,0 @@ -struct Point { - u32 x - u32 y -} \ No newline at end of file