diff --git a/leo/commands/build.rs b/leo/commands/build.rs index ffba966a56..583e41b9c3 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -2,8 +2,8 @@ use crate::{ cli::*, cli_types::*, directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory}, - errors::{BuildError, CLIError}, - files::{ChecksumFile, MainFile, Manifest, MAIN_FILE_NAME}, + errors::CLIError, + files::{ChecksumFile, LibFile, MainFile, Manifest, LIB_FILE_NAME, MAIN_FILE_NAME}, }; use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; @@ -18,7 +18,7 @@ pub struct BuildCommand; impl CLI for BuildCommand { type Options = (); - type Output = (Compiler, bool); + type Output = Option<(Compiler, bool)>; const ABOUT: AboutType = "Compile the current package as a program"; const ARGUMENTS: &'static [ArgumentType] = &[]; @@ -46,58 +46,72 @@ impl CLI for BuildCommand { package_path.pop(); } - // Verify the main file exists - if !MainFile::exists_at(&package_path) { - return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into()); - } + // Compile the package starting with the lib.leo file + if LibFile::exists_at(&package_path) { + // Construct the path to the library file in the source directory + let mut lib_file_path = package_path.clone(); + lib_file_path.push(SOURCE_DIRECTORY_NAME); + lib_file_path.push(LIB_FILE_NAME); - // 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); - - // Load the program at `main_file_path` - let program = Compiler::::new_from_path(package_name.clone(), main_file_path.clone())?; - - // Compute the current program checksum - let program_checksum = program.checksum()?; - - // Generate the program on the constraint system and verify correctness - { - let mut cs = KeypairAssembly:: { - num_inputs: 0, - num_aux: 0, - num_constraints: 0, - at: vec![], - bt: vec![], - ct: vec![], - }; - let temporary_program = program.clone(); - let output = temporary_program.compile_constraints(&mut cs)?; - log::debug!("Compiled constraints - {:#?}", output); - } - - // If a checksum file exists, check if it differs from the new checksum - let checksum_file = ChecksumFile::new(&package_name); - let checksum_differs = if checksum_file.exists_at(&package_path) { - let previous_checksum = checksum_file.read_from(&package_path)?; - program_checksum != previous_checksum - } else { - // By default, the checksum differs if there is no checksum to compare against - true + // Compile the library file but do not output + let _program = Compiler::::new_from_path(package_name.clone(), lib_file_path)?; }; - // If checksum differs, compile the program - if checksum_differs { - // Write the new checksum to the outputs directory - checksum_file.write_to(&path, program_checksum)?; + // Compile the main.leo file along with constraints + if MainFile::exists_at(&package_path) { + // 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); + + // Load the program at `main_file_path` + let program = + Compiler::::new_from_path(package_name.clone(), main_file_path.clone())?; + + // Compute the current program checksum + let program_checksum = program.checksum()?; + + // Generate the program on the constraint system and verify correctness + { + let mut cs = KeypairAssembly:: { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + at: vec![], + bt: vec![], + ct: vec![], + }; + let temporary_program = program.clone(); + let output = temporary_program.compile_constraints(&mut cs)?; + log::debug!("Compiled constraints - {:#?}", output); + } + + // If a checksum file exists, check if it differs from the new checksum + let checksum_file = ChecksumFile::new(&package_name); + let checksum_differs = if checksum_file.exists_at(&package_path) { + let previous_checksum = checksum_file.read_from(&package_path)?; + program_checksum != previous_checksum + } else { + // By default, the checksum differs if there is no checksum to compare against + true + }; + + // If checksum differs, compile the program + if checksum_differs { + // Write the new checksum to the outputs directory + checksum_file.write_to(&path, program_checksum)?; + } + + log::info!("Compiled program in {:?}", main_file_path); + + return Ok(Some((program, checksum_differs))); } - log::info!("Compiled program in {:?}", main_file_path); - - Ok((program, checksum_differs)) + // Return None when compiling a package for publishing + // The published package does not need to have a main.leo + Ok(None) } } diff --git a/leo/commands/deploy.rs b/leo/commands/deploy.rs index cd715498f8..d0b6c7f8a6 100644 --- a/leo/commands/deploy.rs +++ b/leo/commands/deploy.rs @@ -1,4 +1,11 @@ -use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest}; +use crate::{ + cli::*, + cli_types::*, + commands::BuildCommand, + directories::SOURCE_DIRECTORY_NAME, + errors::{CLIError, RunError}, + files::{Manifest, MAIN_FILE_NAME}, +}; use clap::ArgMatches; use std::{convert::TryFrom, env::current_dir}; @@ -24,14 +31,26 @@ impl CLI for DeployCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (_program, _checksum_differs) = BuildCommand::output(options)?; - - // Get the package name let path = current_dir()?; - let _package_name = Manifest::try_from(&path)?.get_package_name(); - log::info!("Unimplemented - `leo deploy`"); + match BuildCommand::output(options)? { + Some((_program, _checksum_differs)) => { + // Get the package name + let _package_name = Manifest::try_from(&path)?.get_package_name(); - Ok(()) + log::info!("Unimplemented - `leo deploy`"); + + Ok(()) + } + None => { + let mut main_file_path = path.clone(); + main_file_path.push(SOURCE_DIRECTORY_NAME); + main_file_path.push(MAIN_FILE_NAME); + + Err(CLIError::RunError(RunError::MainFileDoesNotExist( + main_file_path.into_os_string(), + ))) + } + } } } diff --git a/leo/commands/load.rs b/leo/commands/load.rs index 8cef939d0b..77c8f008b4 100644 --- a/leo/commands/load.rs +++ b/leo/commands/load.rs @@ -1,4 +1,11 @@ -use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest}; +use crate::{ + cli::*, + cli_types::*, + commands::BuildCommand, + directories::SOURCE_DIRECTORY_NAME, + errors::{CLIError, RunError}, + files::{Manifest, MAIN_FILE_NAME}, +}; use clap::ArgMatches; use std::{convert::TryFrom, env::current_dir}; @@ -24,14 +31,26 @@ impl CLI for LoadCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (_program, _checksum_differs) = BuildCommand::output(options)?; - - // Get the package name let path = current_dir()?; - let _package_name = Manifest::try_from(&path)?.get_package_name(); - log::info!("Unimplemented - `leo deploy`"); + match BuildCommand::output(options)? { + Some((_program, _checksum_differs)) => { + // Get the package name + let _package_name = Manifest::try_from(&path)?.get_package_name(); - Ok(()) + log::info!("Unimplemented - `leo load`"); + + Ok(()) + } + None => { + let mut main_file_path = path.clone(); + main_file_path.push(SOURCE_DIRECTORY_NAME); + main_file_path.push(MAIN_FILE_NAME); + + Err(CLIError::RunError(RunError::MainFileDoesNotExist( + main_file_path.into_os_string(), + ))) + } + } } } diff --git a/leo/commands/publish.rs b/leo/commands/publish.rs index 073221904d..905b4c420d 100644 --- a/leo/commands/publish.rs +++ b/leo/commands/publish.rs @@ -30,7 +30,9 @@ impl CLI for PublishCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (_program, _checksum_differs) = BuildCommand::output(options)?; + // Build all program files. + // It's okay if there's just a lib.leo file here + let _output = BuildCommand::output(options)?; // Get the package name let path = current_dir()?; diff --git a/leo/commands/setup.rs b/leo/commands/setup.rs index 8cd115bc3e..fa04f04b65 100644 --- a/leo/commands/setup.rs +++ b/leo/commands/setup.rs @@ -2,8 +2,9 @@ use crate::{ cli::*, cli_types::*, commands::BuildCommand, - errors::{CLIError, VerificationKeyFileError}, - files::{Manifest, ProvingKeyFile, VerificationKeyFile}, + directories::SOURCE_DIRECTORY_NAME, + errors::{CLIError, RunError, VerificationKeyFileError}, + files::{Manifest, ProvingKeyFile, VerificationKeyFile, MAIN_FILE_NAME}, }; use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; @@ -40,66 +41,77 @@ impl CLI for SetupCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (program, checksum_differs) = BuildCommand::output(options)?; - // Get the package name let path = current_dir()?; let package_name = Manifest::try_from(&path)?.get_package_name(); - // Check if a proving key and verification key already exists - let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path) - && VerificationKeyFile::new(&package_name).exists_at(&path); + match BuildCommand::output(options)? { + Some((program, checksum_differs)) => { + // Check if a proving key and verification key already exists + let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path) + && VerificationKeyFile::new(&package_name).exists_at(&path); - // If keys do not exist or the checksum differs, run the program setup - if !keys_exist || checksum_differs { - // Start the timer - let start = Instant::now(); + // If keys do not exist or the checksum differs, run the program setup + if !keys_exist || checksum_differs { + // Start the timer + let start = Instant::now(); - // Run the program setup operation - let rng = &mut thread_rng(); - let parameters = generate_random_parameters::(program.clone(), rng).unwrap(); - let prepared_verifying_key = prepare_verifying_key::(¶meters.vk); + // Run the program setup operation + let rng = &mut thread_rng(); + let parameters = generate_random_parameters::(program.clone(), rng).unwrap(); + let prepared_verifying_key = prepare_verifying_key::(¶meters.vk); - // End the timer - log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis()); + // End the timer + log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis()); - // TODO (howardwu): Convert parameters to a 'proving key' struct for serialization. - // Write the proving key file to the outputs directory - let mut proving_key = vec![]; - parameters.write(&mut proving_key)?; - ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?; + // TODO (howardwu): Convert parameters to a 'proving key' struct for serialization. + // Write the proving key file to the outputs directory + let mut proving_key = vec![]; + parameters.write(&mut proving_key)?; + ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?; - // Write the proving key file to the outputs directory - let mut verification_key = vec![]; - prepared_verifying_key.write(&mut verification_key)?; - VerificationKeyFile::new(&package_name).write_to(&path, &verification_key)?; - } - - // Read the proving key file from the outputs directory - let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?; - let parameters = Parameters::::read(proving_key.as_slice(), true)?; - - // Read the proving key file from the outputs directory - let prepared_verifying_key = prepare_verifying_key::(¶meters.vk); - { - // Load the stored verification key from the outputs directory - let stored_vk = VerificationKeyFile::new(&package_name).read_from(&path)?; - - // Convert the prepared_verifying_key to a buffer - let mut verification_key = vec![]; - prepared_verifying_key.write(&mut verification_key)?; - - // Check that the constructed prepared verification key matches the stored verification key - let compare: Vec<(u8, u8)> = verification_key.into_iter().zip(stored_vk.into_iter()).collect(); - for (a, b) in compare { - if a != b { - return Err(VerificationKeyFileError::IncorrectVerificationKey.into()); + // Write the proving key file to the outputs directory + let mut verification_key = vec![]; + prepared_verifying_key.write(&mut verification_key)?; + VerificationKeyFile::new(&package_name).write_to(&path, &verification_key)?; } + + // Read the proving key file from the outputs directory + let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?; + let parameters = Parameters::::read(proving_key.as_slice(), true)?; + + // Read the proving key file from the outputs directory + let prepared_verifying_key = prepare_verifying_key::(¶meters.vk); + { + // Load the stored verification key from the outputs directory + let stored_vk = VerificationKeyFile::new(&package_name).read_from(&path)?; + + // Convert the prepared_verifying_key to a buffer + let mut verification_key = vec![]; + prepared_verifying_key.write(&mut verification_key)?; + + // Check that the constructed prepared verification key matches the stored verification key + let compare: Vec<(u8, u8)> = verification_key.into_iter().zip(stored_vk.into_iter()).collect(); + for (a, b) in compare { + if a != b { + return Err(VerificationKeyFileError::IncorrectVerificationKey.into()); + } + } + } + + log::info!("Completed program setup"); + + Ok((program, parameters, prepared_verifying_key)) + } + None => { + let mut main_file_path = path.clone(); + main_file_path.push(SOURCE_DIRECTORY_NAME); + main_file_path.push(MAIN_FILE_NAME); + + Err(CLIError::RunError(RunError::MainFileDoesNotExist( + main_file_path.into_os_string(), + ))) } } - - log::info!("Completed program setup"); - - Ok((program, parameters, prepared_verifying_key)) } } diff --git a/leo/commands/unload.rs b/leo/commands/unload.rs index 52fe84b2b0..614acc6428 100644 --- a/leo/commands/unload.rs +++ b/leo/commands/unload.rs @@ -1,4 +1,11 @@ -use crate::{cli::*, cli_types::*, commands::BuildCommand, errors::CLIError, files::Manifest}; +use crate::{ + cli::*, + cli_types::*, + commands::BuildCommand, + directories::SOURCE_DIRECTORY_NAME, + errors::{CLIError, RunError}, + files::{Manifest, MAIN_FILE_NAME}, +}; use clap::ArgMatches; use std::{convert::TryFrom, env::current_dir}; @@ -24,14 +31,26 @@ impl CLI for UnloadCommand { #[cfg_attr(tarpaulin, skip)] fn output(options: Self::Options) -> Result { - let (_program, _checksum_differs) = BuildCommand::output(options)?; - - // Get the package name let path = current_dir()?; - let _package_name = Manifest::try_from(&path)?.get_package_name(); - log::info!("Unimplemented - `leo deploy`"); + match BuildCommand::output(options)? { + Some((_program, _checksum_differs)) => { + // Get the package name + let _package_name = Manifest::try_from(&path)?.get_package_name(); - Ok(()) + log::info!("Unimplemented - `leo load`"); + + Ok(()) + } + None => { + let mut main_file_path = path.clone(); + main_file_path.push(SOURCE_DIRECTORY_NAME); + main_file_path.push(MAIN_FILE_NAME); + + Err(CLIError::RunError(RunError::MainFileDoesNotExist( + main_file_path.into_os_string(), + ))) + } + } } } diff --git a/leo/errors/files/lib.rs b/leo/errors/files/lib.rs new file mode 100644 index 0000000000..a95bc98075 --- /dev/null +++ b/leo/errors/files/lib.rs @@ -0,0 +1,19 @@ +use std::io; + +#[derive(Debug, Error)] +pub enum LibFileError { + #[error("{}: {}", _0, _1)] + Crate(&'static str, String), + + #[error("creating: {}", _0)] + Creating(io::Error), + + #[error("writing: {}", _0)] + Writing(io::Error), +} + +impl From for LibFileError { + fn from(error: std::io::Error) -> Self { + LibFileError::Crate("std::io", format!("{}", error)) + } +} diff --git a/leo/errors/files/mod.rs b/leo/errors/files/mod.rs index d7f66d149d..97bcc19e30 100644 --- a/leo/errors/files/mod.rs +++ b/leo/errors/files/mod.rs @@ -10,6 +10,9 @@ pub use self::gitignore::*; pub mod inputs; pub use self::inputs::*; +pub mod lib; +pub use self::lib::*; + pub mod main; pub use self::main::*; diff --git a/leo/files/lib.rs b/leo/files/lib.rs new file mode 100644 index 0000000000..2b514cc1cf --- /dev/null +++ b/leo/files/lib.rs @@ -0,0 +1,56 @@ +//! The `lib.leo` file. + +use crate::{directories::source::SOURCE_DIRECTORY_NAME, errors::LibFileError}; + +use serde::Deserialize; +use std::{fs::File, io::Write, path::PathBuf}; + +pub static LIB_FILE_NAME: &str = "lib.leo"; + +#[derive(Deserialize)] +pub struct LibFile { + pub package_name: String, +} + +impl LibFile { + 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(SOURCE_DIRECTORY_NAME) { + path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); + } + path.push(PathBuf::from(LIB_FILE_NAME)); + } + path.exists() + } + + pub fn write_to(self, path: &PathBuf) -> Result<(), LibFileError> { + let mut path = path.to_owned(); + if path.is_dir() { + if !path.ends_with(SOURCE_DIRECTORY_NAME) { + path.push(PathBuf::from(SOURCE_DIRECTORY_NAME)); + } + path.push(PathBuf::from(LIB_FILE_NAME)); + } + + let mut file = File::create(&path)?; + Ok(file.write_all(self.template().as_bytes())?) + } + + fn template(&self) -> String { + format!( + r#"// The '{}' lib function. +circuit Circ {{ + c: field +}} +"#, + self.package_name + ) + } +} diff --git a/leo/files/mod.rs b/leo/files/mod.rs index 472330e132..0e4f02c8dc 100644 --- a/leo/files/mod.rs +++ b/leo/files/mod.rs @@ -10,6 +10,9 @@ pub use self::inputs::*; pub mod gitignore; pub use self::gitignore::*; +pub mod lib; +pub use self::lib::*; + pub mod main; pub use self::main::*; diff --git a/types/src/imports/import_symbol.rs b/types/src/imports/import_symbol.rs index 5865df3324..eaef622992 100644 --- a/types/src/imports/import_symbol.rs +++ b/types/src/imports/import_symbol.rs @@ -24,9 +24,9 @@ impl<'ast> From> for ImportSymbol { impl fmt::Display for ImportSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.alias.is_some() { - write!(f, "\t{} as {}", self.symbol, self.alias.as_ref().unwrap()) + write!(f, "{} as {}", self.symbol, self.alias.as_ref().unwrap()) } else { - write!(f, "\t{}", self.symbol) + write!(f, "{}", self.symbol) } } }