Merge pull request #80 from AleoHQ/feature/cli

Add debug flag, improve CLI messages, add num_constraints, add 'leo clean'
This commit is contained in:
Collin Chin 2020-06-29 13:23:58 -07:00 committed by GitHub
commit d1c5c9e928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 183 additions and 70 deletions

View File

@ -53,7 +53,6 @@ pub fn generate_constraints<F: Field + PrimeField, G: GroupType<F>, CS: Constrai
match main.clone() { match main.clone() {
ConstrainedValue::Function(_circuit_identifier, function) => { ConstrainedValue::Function(_circuit_identifier, function) => {
let result = resolved_program.enforce_main_function(cs, program_name, function, parameters)?; let result = resolved_program.enforce_main_function(cs, program_name, function, parameters)?;
log::debug!("{}", result);
Ok(result) Ok(result)
} }
_ => Err(CompilerError::NoMainFunction), _ => Err(CompilerError::NoMainFunction),

View File

@ -15,8 +15,8 @@ circuit PedersenHash {
// The 'pedersen_hash' main function. // The 'pedersen_hash' main function.
function main() -> u32 { function main() -> u32 {
let parameters = [0u32; 512]; const parameters = [0u32; 512];
let pedersen = PedersenHash::new(parameters); const pedersen = PedersenHash::new(parameters);
let input: bool[512] = [true; 512]; let input: bool[512] = [true; 512];
return pedersen.hash(input) return pedersen.hash(input)
} }

View File

@ -1,4 +1,4 @@
use crate::{cli_types::*, errors::CLIError}; use crate::{cli_types::*, errors::CLIError, logger};
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
@ -66,6 +66,19 @@ pub trait CLI {
.subcommands(subcommands) .subcommands(subcommands)
} }
#[cfg_attr(tarpaulin, skip)]
fn process(arguments: &ArgMatches) -> Result<(), CLIError> {
// Set logging environment
match arguments.is_present("debug") {
true => logger::init_logger("leo", 2),
false => logger::init_logger("leo", 1),
}
let options = Self::parse(arguments)?;
let _output = Self::output(options)?;
Ok(())
}
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError>; fn parse(arguments: &ArgMatches) -> Result<Self::Options, CLIError>;

View File

@ -9,6 +9,7 @@ use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType};
use snarkos_algorithms::snark::KeypairAssembly; use snarkos_algorithms::snark::KeypairAssembly;
use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq}; use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq};
use snarkos_models::gadgets::r1cs::ConstraintSystem;
use clap::ArgMatches; use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir}; use std::{convert::TryFrom, env::current_dir};
@ -51,6 +52,8 @@ impl CLI for BuildCommand {
return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into()); return Err(BuildError::MainFileDoesNotExist(package_path.as_os_str().to_owned()).into());
} }
log::info!("Compiling...");
// Create the outputs directory // Create the outputs directory
OutputsDirectory::create(&package_path)?; OutputsDirectory::create(&package_path)?;
@ -78,6 +81,7 @@ impl CLI for BuildCommand {
let temporary_program = program.clone(); let temporary_program = program.clone();
let output = temporary_program.compile_constraints(&mut cs)?; let output = temporary_program.compile_constraints(&mut cs)?;
log::debug!("Compiled constraints - {:#?}", output); log::debug!("Compiled constraints - {:#?}", output);
log::debug!("Number of constraints - {:#?}", cs.num_constraints());
} }
// If a checksum file exists, check if it differs from the new checksum // If a checksum file exists, check if it differs from the new checksum
@ -94,9 +98,11 @@ impl CLI for BuildCommand {
if checksum_differs { if checksum_differs {
// Write the new checksum to the outputs directory // Write the new checksum to the outputs directory
checksum_file.write_to(&path, program_checksum)?; checksum_file.write_to(&path, program_checksum)?;
log::debug!("Checksum saved ({:?})", path);
} }
log::info!("Compiled program in {:?}", main_file_path); log::info!("Compiled program ({:?})", main_file_path);
Ok((program, checksum_differs)) Ok((program, checksum_differs))
} }

50
leo/commands/clean.rs Normal file
View File

@ -0,0 +1,50 @@
use crate::{
cli::*,
cli_types::*,
errors::CLIError,
files::{ChecksumFile, Manifest, ProofFile, ProvingKeyFile, VerificationKeyFile},
};
use clap::ArgMatches;
use std::{convert::TryFrom, env::current_dir};
#[derive(Debug)]
pub struct CleanCommand;
impl CLI for CleanCommand {
type Options = ();
type Output = ();
const ABOUT: AboutType = "Clean the outputs directory";
const ARGUMENTS: &'static [ArgumentType] = &[];
const FLAGS: &'static [FlagType] = &[];
const NAME: NameType = "clean";
const OPTIONS: &'static [OptionType] = &[];
const SUBCOMMANDS: &'static [SubCommandType] = &[];
#[cfg_attr(tarpaulin, skip)]
fn parse(_arguments: &ArgMatches) -> Result<Self::Options, CLIError> {
Ok(())
}
#[cfg_attr(tarpaulin, skip)]
fn output(_options: Self::Options) -> Result<Self::Output, CLIError> {
// Get the package name
let path = current_dir()?;
let package_name = Manifest::try_from(&path)?.get_package_name();
// Remove the checksum from the outputs directory
ChecksumFile::new(&package_name).remove(&path)?;
// Remove the proving key from the outputs directory
ProvingKeyFile::new(&package_name).remove(&path)?;
// Remove the verification key from the outputs directory
VerificationKeyFile::new(&package_name).remove(&path)?;
// Remove the proof from the outputs directory
ProofFile::new(&package_name).remove(&path)?;
Ok(())
}
}

View File

@ -30,7 +30,7 @@ impl CLI for LoadCommand {
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();
log::info!("Unimplemented - `leo deploy`"); log::info!("Unimplemented - `leo load`");
Ok(()) Ok(())
} }

View File

@ -1,6 +1,9 @@
pub mod build; pub mod build;
pub use self::build::*; pub use self::build::*;
pub mod clean;
pub use self::clean::*;
pub mod deploy; pub mod deploy;
pub use self::deploy::*; pub use self::deploy::*;

View File

@ -40,6 +40,8 @@ impl CLI for ProveCommand {
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();
log::info!("Proving...");
// Fetch program inputs here // Fetch program inputs here
let inputs_string = InputsFile::new(&package_name).read_from(&path)?; let inputs_string = InputsFile::new(&package_name).read_from(&path)?;
program.parse_inputs(&inputs_string)?; program.parse_inputs(&inputs_string)?;

View File

@ -52,6 +52,8 @@ impl CLI for SetupCommand {
// 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 {
log::info!("Setup starting...");
// Start the timer // Start the timer
let start = Instant::now(); let start = Instant::now();
@ -61,18 +63,25 @@ impl CLI for SetupCommand {
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()); let end = 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)?;
log::info!("Saving proving key ({:?})", path);
// Write the proving key file to the outputs directory // Write the verification 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)?;
log::info!("Saving verification key ({:?})", path);
// Output the setup time
log::info!("Setup completed in {:?} milliseconds", end);
} else {
log::info!("Setup complete");
} }
// Read the proving key file from the outputs directory // Read the proving key file from the outputs directory
@ -98,7 +107,7 @@ impl CLI for SetupCommand {
} }
} }
log::info!("Completed program setup"); log::info!("Program setup complete");
Ok((program, parameters, prepared_verifying_key)) Ok((program, parameters, prepared_verifying_key))
} }

View File

@ -30,7 +30,7 @@ impl CLI for UnloadCommand {
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();
log::info!("Unimplemented - `leo deploy`"); log::info!("Unimplemented - `leo unload`");
Ok(()) Ok(())
} }

View File

@ -11,6 +11,9 @@ pub enum ChecksumFileError {
#[error("Cannot read from the provided file path - {:?}", _0)] #[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf), FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)] #[error("writing: {}", _0)]
Writing(io::Error), Writing(io::Error),
} }

View File

@ -11,6 +11,9 @@ pub enum ProofFileError {
#[error("Cannot read from the provided file path - {:?}", _0)] #[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf), FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)] #[error("writing: {}", _0)]
Writing(io::Error), Writing(io::Error),
} }

View File

@ -11,6 +11,9 @@ pub enum ProvingKeyFileError {
#[error("Cannot read from the provided file path - {:?}", _0)] #[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf), FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("writing: {}", _0)] #[error("writing: {}", _0)]
Writing(io::Error), Writing(io::Error),
} }

View File

@ -11,6 +11,9 @@ pub enum VerificationKeyFileError {
#[error("Cannot read from the provided file path - {:?}", _0)] #[error("Cannot read from the provided file path - {:?}", _0)]
FileReadError(PathBuf), FileReadError(PathBuf),
#[error("Cannot remove the provided file - {:?}", _0)]
FileRemovalError(PathBuf),
#[error("Verification key file was corrupted")] #[error("Verification key file was corrupted")]
IncorrectVerificationKey, IncorrectVerificationKey,

View File

@ -42,11 +42,21 @@ impl ChecksumFile {
let mut file = File::create(&path)?; let mut file = File::create(&path)?;
file.write_all(checksum.as_bytes())?; file.write_all(checksum.as_bytes())?;
log::info!("Checksum stored to {:?}", path);
Ok(()) Ok(())
} }
/// Removes the checksum at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ChecksumFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ChecksumFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf { fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned(); let mut path = path.to_owned();
if path.is_dir() { if path.is_dir() {

View File

@ -43,11 +43,23 @@ impl ProofFile {
let mut file = File::create(&path)?; let mut file = File::create(&path)?;
file.write_all(proof)?; file.write_all(proof)?;
log::info!("Proof stored to {:?}", path); log::info!("Proof stored ({:?})", path);
Ok(()) Ok(())
} }
/// Removes the proof at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ProofFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ProofFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf { fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned(); let mut path = path.to_owned();
if path.is_dir() { if path.is_dir() {

View File

@ -42,11 +42,21 @@ impl ProvingKeyFile {
let mut file = File::create(&path)?; let mut file = File::create(&path)?;
file.write_all(proving_key)?; file.write_all(proving_key)?;
log::info!("Proving key stored to {:?}", path);
Ok(()) Ok(())
} }
/// Removes the proving key at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, ProvingKeyFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| ProvingKeyFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf { fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned(); let mut path = path.to_owned();
if path.is_dir() { if path.is_dir() {

View File

@ -42,11 +42,21 @@ impl VerificationKeyFile {
let mut file = File::create(&path)?; let mut file = File::create(&path)?;
file.write_all(verification_key)?; file.write_all(verification_key)?;
log::info!("Verification key stored to {:?}", path);
Ok(()) Ok(())
} }
/// Removes the verification key at the given path if it exists. Returns `true` on success,
/// `false` if the file doesn't exist, and `Error` if the file system fails during operation.
pub fn remove(&self, path: &PathBuf) -> Result<bool, VerificationKeyFileError> {
let path = self.setup_file_path(path);
if !path.exists() {
return Ok(false);
}
fs::remove_file(&path).map_err(|_| VerificationKeyFileError::FileRemovalError(path.clone()))?;
Ok(true)
}
fn setup_file_path(&self, path: &PathBuf) -> PathBuf { fn setup_file_path(&self, path: &PathBuf) -> PathBuf {
let mut path = path.to_owned(); let mut path = path.to_owned();
if path.is_dir() { if path.is_dir() {

View File

@ -3,33 +3,18 @@ use std::io::Write;
const LEVEL_NAME_LENGTH: usize = 10; 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(),
}
}
#[allow(dead_code)] #[allow(dead_code)]
fn colored_string(level: log::Level, message: &str) -> colored::ColoredString { fn colored_string(level: log::Level, message: &str) -> colored::ColoredString {
match level { match level {
log::Level::Error => message.bold().red(), log::Level::Error => message.bold().red(),
log::Level::Warn => message.bold().yellow(), log::Level::Warn => message.bold().yellow(),
log::Level::Info => message.bold().blue(), log::Level::Info => message.bold().cyan(),
log::Level::Debug => message.bold().magenta(), log::Level::Debug => message.bold().magenta(),
log::Level::Trace => message.bold(), log::Level::Trace => message.bold(),
} }
} }
/// Initialize logger with custom format and verbosity. /// 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) { pub fn init_logger(app_name: &'static str, verbosity: usize) {
env_logger::builder() env_logger::builder()
.filter_level(match verbosity { .filter_level(match verbosity {
@ -46,8 +31,7 @@ pub fn init_logger(app_name: &'static str, verbosity: usize) {
writeln!( writeln!(
buf, buf,
"{:>5}{:>5} {}", "{:>5} {}",
level_string(record.level()),
colored_string(record.level(), app_name), colored_string(record.level(), app_name),
record.args().to_string().replace("\n", &padding) record.args().to_string().replace("\n", &padding)
) )

View File

@ -1,11 +1,9 @@
use leo::{cli::*, commands::*, errors::CLIError, logger}; use leo::{cli::*, commands::*, errors::CLIError};
use clap::{App, AppSettings}; use clap::{App, AppSettings, Arg};
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
fn main() -> Result<(), CLIError> { fn main() -> Result<(), CLIError> {
logger::init_logger("leo", 1);
let arguments = App::new("leo") let arguments = App::new("leo")
.version("v0.1.0") .version("v0.1.0")
.about("Leo compiler and package manager") .about("Leo compiler and package manager")
@ -16,46 +14,41 @@ fn main() -> Result<(), CLIError> {
AppSettings::DisableVersion, AppSettings::DisableVersion,
AppSettings::SubcommandRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp,
]) ])
.args(&[Arg::with_name("debug")
.short("d")
.long("debug")
.help("Enables debugging mode")
.global(true)])
.subcommands(vec![ .subcommands(vec![
NewCommand::new().display_order(0), NewCommand::new().display_order(0),
InitCommand::new().display_order(1), InitCommand::new().display_order(1),
BuildCommand::new().display_order(2), BuildCommand::new().display_order(2),
LoadCommand::new().display_order(3), TestCommand::new().display_order(3),
UnloadCommand::new().display_order(4), LoadCommand::new().display_order(4),
SetupCommand::new().display_order(5), UnloadCommand::new().display_order(5),
ProveCommand::new().display_order(6), SetupCommand::new().display_order(6),
RunCommand::new().display_order(7), ProveCommand::new().display_order(7),
PublishCommand::new().display_order(8), RunCommand::new().display_order(8),
DeployCommand::new().display_order(9), PublishCommand::new().display_order(9),
TestCommand::new().display_order(10), DeployCommand::new().display_order(10),
CleanCommand::new().display_order(11),
]) ])
.set_term_width(0) .set_term_width(0)
.get_matches(); .get_matches();
match arguments.subcommand() { match arguments.subcommand() {
("new", Some(arguments)) => NewCommand::output(NewCommand::parse(arguments)?), ("new", Some(arguments)) => NewCommand::process(arguments),
("init", Some(arguments)) => InitCommand::output(InitCommand::parse(arguments)?), ("init", Some(arguments)) => InitCommand::process(arguments),
("build", Some(arguments)) => { ("build", Some(arguments)) => BuildCommand::process(arguments),
BuildCommand::output(BuildCommand::parse(arguments)?)?; ("test", Some(arguments)) => TestCommand::process(arguments),
Ok(()) ("load", Some(arguments)) => LoadCommand::process(arguments),
} ("unload", Some(arguments)) => UnloadCommand::process(arguments),
("load", Some(arguments)) => LoadCommand::output(LoadCommand::parse(arguments)?), ("setup", Some(arguments)) => SetupCommand::process(arguments),
("unload", Some(arguments)) => UnloadCommand::output(UnloadCommand::parse(arguments)?), ("prove", Some(arguments)) => ProveCommand::process(arguments),
("setup", Some(arguments)) => { ("run", Some(arguments)) => RunCommand::process(arguments),
SetupCommand::output(SetupCommand::parse(arguments)?)?; ("publish", Some(arguments)) => PublishCommand::process(arguments),
Ok(()) ("deploy", Some(arguments)) => DeployCommand::process(arguments),
} ("clean", Some(arguments)) => CleanCommand::process(arguments),
("prove", Some(arguments)) => {
ProveCommand::output(ProveCommand::parse(arguments)?)?;
Ok(())
}
("run", Some(arguments)) => RunCommand::output(RunCommand::parse(arguments)?),
("publish", Some(arguments)) => PublishCommand::output(PublishCommand::parse(arguments)?),
("deploy", Some(arguments)) => DeployCommand::output(DeployCommand::parse(arguments)?),
("test", Some(arguments)) => {
TestCommand::output(TestCommand::parse(arguments)?)?;
Ok(())
}
_ => unreachable!(), _ => unreachable!(),
} }
} }