From 504480b3d75d88dcd3f9878496a4fc18c9cc09f8 Mon Sep 17 00:00:00 2001 From: collin Date: Wed, 24 Jun 2020 21:05:11 -0700 Subject: [PATCH] serialize program into bytes file --- Cargo.lock | 1 + compiler/Cargo.toml | 1 + compiler/src/compiler.rs | 29 ++++++++++++---- compiler/src/errors/compiler.rs | 4 +++ leo/commands/build.rs | 27 ++++++++++++++- leo/commands/test.rs | 2 +- leo/errors/cli.rs | 10 ++++++ leo/errors/files/bytes.rs | 22 ++++++++++++ leo/errors/files/mod.rs | 3 ++ leo/files/bytes.rs | 60 +++++++++++++++++++++++++++++++++ leo/files/mod.rs | 3 ++ 11 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 leo/errors/files/bytes.rs create mode 100644 leo/files/bytes.rs diff --git a/Cargo.lock b/Cargo.lock index f0d0e16128..166257d9f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,6 +511,7 @@ dependencies = [ name = "leo-compiler" version = "0.1.0" dependencies = [ + "bincode", "hex", "leo-ast", "leo-inputs", diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index 3a528a946c..cd08af71e0 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -14,6 +14,7 @@ snarkos-errors = { path = "../../snarkOS/errors", version = "0.8.0", default-fea snarkos-gadgets = { path = "../../snarkOS/gadgets", version = "0.8.0", default-features = false } snarkos-models = { path = "../../snarkOS/models", version = "0.8.0", default-features = false } +bincode = { version = "1.0" } hex = { version = "0.4.2" } log = { version = "0.4" } pest = { version = "2.0" } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 4b5ea8be63..4a630f732d 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -40,15 +40,15 @@ impl> Compiler { } } - pub fn init(package_name: String, main_file_path: PathBuf) -> Result { - let mut program = Self::new(package_name); - program.set_path(main_file_path); + pub fn new_from_path(package_name: String, main_file_path: PathBuf) -> Result { + let mut compiler = Self::new(package_name); + compiler.set_path(main_file_path); // Generate the abstract syntax tree and assemble the program - let program_string = program.load_program()?; - program.parse_program(&program_string)?; + let program_string = compiler.load_program()?; + compiler.parse_program(&program_string)?; - Ok(program) + Ok(compiler) } pub fn set_path(&mut self, main_file_path: PathBuf) { @@ -116,6 +116,23 @@ impl> Compiler { Ok(()) } + + pub fn to_bytes(&self) -> Result, CompilerError> { + Ok(bincode::serialize(&self.program)?) + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let program: Program = bincode::deserialize(bytes)?; + + Ok(Self { + package_name: program.name.clone(), + main_file_path: PathBuf::new(), + program, + program_inputs: Inputs::new(), + output: None, + _engine: PhantomData, + }) + } } impl> ConstraintSynthesizer for Compiler { diff --git a/compiler/src/errors/compiler.rs b/compiler/src/errors/compiler.rs index 90ba339810..9f086b6b01 100644 --- a/compiler/src/errors/compiler.rs +++ b/compiler/src/errors/compiler.rs @@ -2,6 +2,7 @@ use crate::errors::{FunctionError, ImportError}; use leo_ast::ParserError; use leo_inputs::InputParserError; +use bincode::Error as SerdeError; use std::path::PathBuf; #[derive(Debug, Error)] @@ -26,6 +27,9 @@ pub enum CompilerError { #[error("{}", _0)] ParserError(#[from] ParserError), + + #[error("{}", _0)] + SerdeError(#[from] SerdeError), } impl CompilerError { diff --git a/leo/commands/build.rs b/leo/commands/build.rs index 6011e13f57..727437510b 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -10,6 +10,7 @@ use leo_compiler::{compiler::Compiler, group::edwards_bls12::EdwardsGroupType}; use snarkos_algorithms::snark::KeypairAssembly; use snarkos_curves::{bls12_377::Bls12_377, edwards_bls12::Fq}; +use crate::files::BytesFile; use clap::ArgMatches; use std::{convert::TryFrom, env::current_dir}; @@ -59,8 +60,32 @@ impl CLI for BuildCommand { main_file_path.push(SOURCE_DIRECTORY_NAME); main_file_path.push(MAIN_FILE_NAME); + // Check if the program bytes exist + let existing_bytes = BytesFile::new(&package_name).exists_at(&path); + + let program = if existing_bytes { + // Load the program ast from stored bytes + let bytes = BytesFile::new(&package_name).read_from(&path)?; + + let mut program = Compiler::::from_bytes(bytes.as_slice())?; + + program.set_path(main_file_path.clone()); + + program + } else { + // Load the program at `main_file_path` + let program = + Compiler::::new_from_path(package_name.clone(), main_file_path.clone())?; + + // Store the program ast as bytes + let bytes = program.to_bytes()?; + + BytesFile::new(&package_name).write_to(&path, bytes)?; + + program + }; + // Compute the current program checksum - let program = Compiler::::init(package_name.clone(), main_file_path.clone())?; let program_checksum = program.checksum()?; // Generate the program on the constraint system and verify correctness diff --git a/leo/commands/test.rs b/leo/commands/test.rs index 835c1a3919..cbff24c38e 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -57,7 +57,7 @@ impl CLI for TestCommand { main_file_path.push(MAIN_FILE_NAME); // Compute the current program checksum - let program = Compiler::::init(package_name.clone(), main_file_path.clone())?; + let program = Compiler::::new_from_path(package_name.clone(), main_file_path.clone())?; // Generate the program on the constraint system and verify correctness { diff --git a/leo/errors/cli.rs b/leo/errors/cli.rs index a1e4cb1957..80dbe4ce5d 100644 --- a/leo/errors/cli.rs +++ b/leo/errors/cli.rs @@ -5,6 +5,9 @@ pub enum CLIError { #[error("{}", _0)] BuildError(BuildError), + #[error("{}", _0)] + BytesFileError(BytesFileError), + #[error("{}: {}", _0, _1)] Crate(&'static str, String), @@ -54,6 +57,13 @@ pub enum CLIError { VerificationKeyFileError(VerificationKeyFileError), } +impl From for CLIError { + fn from(error: BytesFileError) -> Self { + log::error!("{}\n", error); + CLIError::BytesFileError(error) + } +} + impl From for CLIError { fn from(error: BuildError) -> Self { log::error!("{}\n", error); diff --git a/leo/errors/files/bytes.rs b/leo/errors/files/bytes.rs new file mode 100644 index 0000000000..11d90369e9 --- /dev/null +++ b/leo/errors/files/bytes.rs @@ -0,0 +1,22 @@ +use std::{io, path::PathBuf}; + +#[derive(Debug, Error)] +pub enum BytesFileError { + #[error("{}: {}", _0, _1)] + Crate(&'static str, String), + + #[error("creating: {}", _0)] + Creating(io::Error), + + #[error("Cannot read from the provided file path - {:?}", _0)] + FileReadError(PathBuf), + + #[error("writing: {}", _0)] + Writing(io::Error), +} + +impl From for BytesFileError { + fn from(error: std::io::Error) -> Self { + BytesFileError::Crate("std::io", format!("{}", error)) + } +} diff --git a/leo/errors/files/mod.rs b/leo/errors/files/mod.rs index c33f941ef7..6300c96a14 100644 --- a/leo/errors/files/mod.rs +++ b/leo/errors/files/mod.rs @@ -1,3 +1,6 @@ +pub mod bytes; +pub use self::bytes::*; + pub mod checksum; pub use self::checksum::*; diff --git a/leo/files/bytes.rs b/leo/files/bytes.rs new file mode 100644 index 0000000000..3a486ba1bc --- /dev/null +++ b/leo/files/bytes.rs @@ -0,0 +1,60 @@ +//! The program bytes file. + +use crate::{directories::outputs::OUTPUTS_DIRECTORY_NAME, errors::BytesFileError}; + +use serde::Deserialize; +use std::{ + fs::{self, File}, + io::Write, + path::PathBuf, +}; + +pub static BYTES_FILE_EXTENSION: &str = ".bytes"; + +#[derive(Deserialize)] +pub struct BytesFile { + pub package_name: String, +} + +impl BytesFile { + pub fn new(package_name: &str) -> Self { + Self { + package_name: package_name.to_string(), + } + } + + pub fn exists_at(&self, path: &PathBuf) -> bool { + let path = self.setup_file_path(path); + path.exists() + } + + /// Reads the program bytes from the given file path if it exists. + pub fn read_from(&self, path: &PathBuf) -> Result, BytesFileError> { + let path = self.setup_file_path(path); + + Ok(fs::read(&path).map_err(|_| BytesFileError::FileReadError(path.clone()))?) + } + + /// Writes the given program bytes to a file. + pub fn write_to(&self, path: &PathBuf, bytes: Vec) -> Result<(), BytesFileError> { + let path = self.setup_file_path(path); + + let mut file = File::create(&path)?; + file.write_all(bytes.as_slice())?; + + log::info!("program bytes stored to {:?}", path); + + Ok(()) + } + + fn setup_file_path(&self, path: &PathBuf) -> PathBuf { + let mut path = path.to_owned(); + if path.is_dir() { + if !path.ends_with(OUTPUTS_DIRECTORY_NAME) { + path.push(PathBuf::from(OUTPUTS_DIRECTORY_NAME)); + } + path.push(PathBuf::from(format!("{}{}", self.package_name, BYTES_FILE_EXTENSION))); + } + path + } +} diff --git a/leo/files/mod.rs b/leo/files/mod.rs index 6ef50f807b..bd2fae1ed4 100644 --- a/leo/files/mod.rs +++ b/leo/files/mod.rs @@ -1,3 +1,6 @@ +pub mod bytes; +pub use self::bytes::*; + pub mod checksum; pub use self::checksum::*;