add lib.leo support

This commit is contained in:
collin 2020-06-26 20:58:43 -07:00
parent 8825a0741c
commit 9c2a0e4ec6
11 changed files with 292 additions and 126 deletions

View File

@ -2,8 +2,8 @@ use crate::{
cli::*, cli::*,
cli_types::*, cli_types::*,
directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory}, directories::{source::SOURCE_DIRECTORY_NAME, OutputsDirectory},
errors::{BuildError, CLIError}, errors::CLIError,
files::{ChecksumFile, MainFile, Manifest, MAIN_FILE_NAME}, files::{ChecksumFile, LibFile, MainFile, Manifest, LIB_FILE_NAME, MAIN_FILE_NAME},
}; };
use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType};
@ -18,7 +18,7 @@ pub struct BuildCommand;
impl CLI for BuildCommand { impl CLI for BuildCommand {
type Options = (); type Options = ();
type Output = (Compiler<Fq, EdwardsGroupType>, bool); type Output = Option<(Compiler<Fq, EdwardsGroupType>, bool)>;
const ABOUT: AboutType = "Compile the current package as a program"; const ABOUT: AboutType = "Compile the current package as a program";
const ARGUMENTS: &'static [ArgumentType] = &[]; const ARGUMENTS: &'static [ArgumentType] = &[];
@ -46,58 +46,72 @@ impl CLI for BuildCommand {
package_path.pop(); package_path.pop();
} }
// Verify the main file exists // Compile the package starting with the lib.leo file
if !MainFile::exists_at(&package_path) { if LibFile::exists_at(&package_path) {
return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into()); // 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 // Compile the library file but do not output
OutputsDirectory::create(&package_path)?; let _program = Compiler::<Fq, EdwardsGroupType>::new_from_path(package_name.clone(), lib_file_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::<Fq, EdwardsGroupType>::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::<Bls12_377> {
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 // Compile the main.leo file along with constraints
if checksum_differs { if MainFile::exists_at(&package_path) {
// Write the new checksum to the outputs directory // Create the outputs directory
checksum_file.write_to(&path, program_checksum)?; 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::<Fq, EdwardsGroupType>::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::<Bls12_377> {
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); // Return None when compiling a package for publishing
// The published package does not need to have a main.leo
Ok((program, checksum_differs)) Ok(None)
} }
} }

View File

@ -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 clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir}; use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,26 @@ impl CLI for DeployCommand {
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> { fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?; 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(),
)))
}
}
} }
} }

View File

@ -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 clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir}; use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,26 @@ impl CLI for LoadCommand {
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> { fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?; 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(),
)))
}
}
} }
} }

View File

@ -30,7 +30,9 @@ impl CLI for PublishCommand {
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> { fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
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 // Get the package name
let path = current_dir()?; let path = current_dir()?;

View File

@ -2,8 +2,9 @@ use crate::{
cli::*, cli::*,
cli_types::*, cli_types::*,
commands::BuildCommand, commands::BuildCommand,
errors::{CLIError, VerificationKeyFileError}, directories::SOURCE_DIRECTORY_NAME,
files::{Manifest, ProvingKeyFile, VerificationKeyFile}, errors::{CLIError, RunError, VerificationKeyFileError},
files::{Manifest, ProvingKeyFile, VerificationKeyFile, MAIN_FILE_NAME},
}; };
use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType};
@ -40,66 +41,77 @@ impl CLI for SetupCommand {
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> { fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (program, checksum_differs) = BuildCommand::output(options)?;
// Get the package name // Get the package name
let path = current_dir()?; let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name(); let package_name = Manifest::try_from(&path)?.get_package_name();
// Check if a proving key and verification key already exists match BuildCommand::output(options)? {
let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path) Some((program, checksum_differs)) => {
&& VerificationKeyFile::new(&package_name).exists_at(&path); // 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 do not exist or the checksum differs, run the program setup
if !keys_exist || checksum_differs { if !keys_exist || checksum_differs {
// Start the timer // Start the timer
let start = Instant::now(); let start = Instant::now();
// Run the program setup operation // Run the program setup operation
let rng = &mut thread_rng(); let rng = &mut thread_rng();
let parameters = generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap(); let parameters = generate_random_parameters::<Bls12_377, _, _>(program.clone(), rng).unwrap();
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk); let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.vk);
// End the timer // End the timer
log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis()); log::info!("Setup completed in {:?} milliseconds", start.elapsed().as_millis());
// TODO (howardwu): Convert parameters to a 'proving key' struct for serialization. // TODO (howardwu): Convert parameters to a 'proving key' struct for serialization.
// Write the proving key file to the outputs directory // Write the proving key file to the outputs directory
let mut proving_key = vec![]; let mut proving_key = vec![];
parameters.write(&mut proving_key)?; parameters.write(&mut proving_key)?;
ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?; ProvingKeyFile::new(&package_name).write_to(&path, &proving_key)?;
// Write the proving key file to the outputs directory // Write the proving key file to the outputs directory
let mut verification_key = vec![]; let mut verification_key = vec![];
prepared_verifying_key.write(&mut verification_key)?; prepared_verifying_key.write(&mut verification_key)?;
VerificationKeyFile::new(&package_name).write_to(&path, &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::<Bls12_377>::read(proving_key.as_slice(), true)?;
// Read the proving key file from the outputs directory
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.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());
} }
// Read the proving key file from the outputs directory
let proving_key = ProvingKeyFile::new(&package_name).read_from(&path)?;
let parameters = Parameters::<Bls12_377>::read(proving_key.as_slice(), true)?;
// Read the proving key file from the outputs directory
let prepared_verifying_key = prepare_verifying_key::<Bls12_377>(&parameters.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))
} }
} }

View File

@ -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 clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir}; use std::{convert::TryFrom, env::current_dir};
@ -24,14 +31,26 @@ impl CLI for UnloadCommand {
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn output(options: Self::Options) -> Result<Self::Output, CLIError> { fn output(options: Self::Options) -> Result<Self::Output, CLIError> {
let (_program, _checksum_differs) = BuildCommand::output(options)?;
// Get the package name
let path = current_dir()?; 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(),
)))
}
}
} }
} }

19
leo/errors/files/lib.rs Normal file
View File

@ -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<std::io::Error> for LibFileError {
fn from(error: std::io::Error) -> Self {
LibFileError::Crate("std::io", format!("{}", error))
}
}

View File

@ -10,6 +10,9 @@ pub use self::gitignore::*;
pub mod inputs; pub mod inputs;
pub use self::inputs::*; pub use self::inputs::*;
pub mod lib;
pub use self::lib::*;
pub mod main; pub mod main;
pub use self::main::*; pub use self::main::*;

56
leo/files/lib.rs Normal file
View File

@ -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
)
}
}

View File

@ -10,6 +10,9 @@ pub use self::inputs::*;
pub mod gitignore; pub mod gitignore;
pub use self::gitignore::*; pub use self::gitignore::*;
pub mod lib;
pub use self::lib::*;
pub mod main; pub mod main;
pub use self::main::*; pub use self::main::*;

View File

@ -24,9 +24,9 @@ impl<'ast> From<AstImportSymbol<'ast>> for ImportSymbol {
impl fmt::Display for ImportSymbol { impl fmt::Display for ImportSymbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.alias.is_some() { 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 { } else {
write!(f, "\t{}", self.symbol) write!(f, "{}", self.symbol)
} }
} }
} }