diff --git a/CHANGELOG.md b/CHANGELOG.md index dd3baa3b74..6c630e1e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.7.0 (TBD) +## 0.7.0 (2023-10-11) #### Assembly - Added ability to attach doc comments to re-exported procedures (#994). @@ -12,6 +12,9 @@ - Added `debug` decorator (#1069). - Refactored `push` instruction so now it parses long hex string in little-endian (#1076). +#### CLI +- Implemented ability to output compiled `.masb` files to disk (#1102). + #### VM Internals - Simplified range checker and removed 1 main and 1 auxiliary trace column (#949). - Migrated range checker lookups to use LogUp and reduced the number of trace columns to 2 main and @@ -23,9 +26,11 @@ - Added `TraceLenSummary` struct which holds information about traces lengths to the `ExecutionTrace` (#1029). - Imposed the 2^32 limit for the memory addresses used in the memory chiplet (#1049). - Supported `PartialMerkleTree` as a secret input in `.input` file (#1072). +- [BREAKING] Refactored `AdviceProvider` interface into [Host] interface (#1082). #### Stdlib - Completed `std::collections::smt` module by implementing `insert` and `set` procedures (#1036, #1038, #1046). +- Added new module `std::crypto::dsa::rpo_falcon512` to support Falcon signature verification (#1000, #1094) ## 0.6.1 (2023-06-29) diff --git a/assembly/src/assembler/mod.rs b/assembly/src/assembler/mod.rs index 8dbd584fb0..af92112779 100644 --- a/assembly/src/assembler/mod.rs +++ b/assembly/src/assembler/mod.rs @@ -130,9 +130,19 @@ impl Assembler { let source = source.as_ref(); let program = ProgramAst::parse(source)?; + // compile the program and return + self.compile_ast(&program) + } + + /// Compiles the provided abstract syntax tree into a [Program]. The resulting program can be + /// executed on Miden VM. + /// + /// # Errors + /// Returns an error if the compilation of the specified program fails. + pub fn compile_ast(&self, program: &ProgramAst) -> Result { // compile the program - let mut context = AssemblyContext::for_program(Some(&program)); - let program_root = self.compile_in_context(&program, &mut context)?; + let mut context = AssemblyContext::for_program(Some(program)); + let program_root = self.compile_in_context(program, &mut context)?; // convert the context into a call block table for the program let cb_table = context.into_cb_table(&self.proc_cache.borrow())?; diff --git a/assembly/src/ast/mod.rs b/assembly/src/ast/mod.rs index f423d1b6f1..24e2d746b7 100644 --- a/assembly/src/ast/mod.rs +++ b/assembly/src/ast/mod.rs @@ -10,6 +10,8 @@ use super::{ MAX_LABEL_LEN, }; use core::{fmt, iter, str::from_utf8}; +#[cfg(feature = "std")] +use std::{fs, io, path::Path}; use vm_core::utils::bound_into_included_u64; pub use super::tokens::SourceLocation; @@ -336,6 +338,26 @@ impl ProgramAst { pub fn clear_imports(&mut self) { self.import_info = None; } + + // WRITE TO FILE + // -------------------------------------------------------------------------------------------- + + /// Writes ProgramAst to provided file path + #[cfg(feature = "std")] + pub fn write_to_file

(&self, file_path: P) -> io::Result<()> + where + P: AsRef, + { + let path = file_path.as_ref(); + if let Some(dir) = path.parent() { + fs::create_dir_all(dir)?; + } + + let bytes = self.to_bytes(AstSerdeOptions { + serialize_imports: true, + }); + fs::write(path, bytes) + } } impl fmt::Display for ProgramAst { diff --git a/miden/src/cli/compile.rs b/miden/src/cli/compile.rs index 9eb5018b18..7b0de2e6ce 100644 --- a/miden/src/cli/compile.rs +++ b/miden/src/cli/compile.rs @@ -12,6 +12,9 @@ pub struct CompileCmd { /// Paths to .masl library files #[clap(short = 'l', long = "libraries", value_parser)] library_paths: Vec, + /// Path to output file + #[clap(short = 'o', long = "output", value_parser)] + output_file: Option, } impl CompileCmd { @@ -20,16 +23,20 @@ impl CompileCmd { println!("Compile program"); println!("============================================================"); + // load the program from file and parse it + let program = ProgramFile::read(&self.assembly_file)?; + // load libraries from files let libraries = Libraries::new(&self.library_paths)?; - // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; + // compile the program + let compiled_program = program.compile(&Debug::Off, libraries.libraries)?; // report program hash to user - let program_hash: [u8; 32] = program.hash().into(); + let program_hash: [u8; 32] = compiled_program.hash().into(); println!("program hash is {}", hex::encode(program_hash)); - Ok(()) + // write the compiled file + program.write(self.output_file.clone()) } } diff --git a/miden/src/cli/data.rs b/miden/src/cli/data.rs index 17267b62ad..aef6b6b691 100644 --- a/miden/src/cli/data.rs +++ b/miden/src/cli/data.rs @@ -3,8 +3,8 @@ use miden::{ crypto::{MerkleStore, MerkleTree, NodeIndex, PartialMerkleTree, RpoDigest, SimpleSmt}, math::Felt, utils::{Deserializable, SliceReader}, - AdviceInputs, Assembler, Digest, ExecutionProof, MemAdviceProvider, Program, StackInputs, - StackOutputs, Word, + AdviceInputs, Assembler, Digest, ExecutionProof, MemAdviceProvider, Program, ProgramAst, + StackInputs, StackOutputs, Word, }; use serde_derive::{Deserialize, Serialize}; use std::{ @@ -69,7 +69,7 @@ pub enum MerkleData { pub struct InputFile { /// String representation of the initial operand stack, composed of chained field elements. pub operand_stack: Vec, - /// Opitonal string representation of the initial advice stack, composed of chained field + /// Optional string representation of the initial advice stack, composed of chained field /// elements. pub advice_stack: Option>, /// Optional map of 32 byte hex strings to vectors of u64s representing the initial advice map. @@ -371,21 +371,40 @@ impl OutputFile { // PROGRAM FILE // ================================================================================================ -pub struct ProgramFile; +pub struct ProgramFile { + ast: ProgramAst, + path: PathBuf, +} -/// Helper methods to interact with masm program file +/// Helper methods to interact with masm program file. impl ProgramFile { - pub fn read(path: &PathBuf, debug: &Debug, libraries: I) -> Result + /// Reads the masm file at the specified path and parses it into a [ProgramAst]. + pub fn read(path: &PathBuf) -> Result { + // read program file to string + println!("Reading program file `{}`", path.display()); + let source = fs::read_to_string(&path) + .map_err(|err| format!("Failed to open program file `{}` - {}", path.display(), err))?; + + // parse the program into an AST + print!("Parsing program... "); + let now = Instant::now(); + let ast = ProgramAst::parse(&source).map_err(|err| { + format!("Failed to parse program file `{}` - {}", path.display(), err) + })?; + println!("done ({} ms)", now.elapsed().as_millis()); + + Ok(Self { + ast, + path: path.clone(), + }) + } + + /// Compiles this program file into a [Program]. + pub fn compile(&self, debug: &Debug, libraries: I) -> Result where I: IntoIterator, L: Library, { - println!("Reading program file `{}`", path.display()); - - // read program file to string - let program_file = fs::read_to_string(&path) - .map_err(|err| format!("Failed to open program file `{}` - {}", path.display(), err))?; - print!("Compiling program... "); let now = Instant::now(); @@ -400,13 +419,27 @@ impl ProgramFile { .map_err(|err| format!("Failed to load libraries `{}`", err))?; let program = assembler - .compile(&program_file) + .compile_ast(&self.ast) .map_err(|err| format!("Failed to compile program - {}", err))?; println!("done ({} ms)", now.elapsed().as_millis()); Ok(program) } + + /// Writes this file into the specified path, if one is provided. If the path is not provided, + /// writes the file into the same directory as the source file, but with `.masb` extension. + pub fn write(&self, out_path: Option) -> Result<(), String> { + let out_path = out_path.unwrap_or_else(|| { + let mut out_file = self.path.clone(); + out_file.set_extension("masb"); + out_file + }); + + self.ast + .write_to_file(out_path) + .map_err(|err| format!("Failed to write the compiled file: {err}")) + } } // PROOF FILE diff --git a/miden/src/cli/debug/mod.rs b/miden/src/cli/debug/mod.rs index 96d5c0a676..bd683fde29 100644 --- a/miden/src/cli/debug/mod.rs +++ b/miden/src/cli/debug/mod.rs @@ -36,7 +36,8 @@ impl DebugCmd { let libraries = Libraries::new(&self.library_paths)?; // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, &Debug::On, libraries.libraries)?; + let program = + ProgramFile::read(&self.assembly_file)?.compile(&Debug::On, libraries.libraries)?; let program_hash: [u8; 32] = program.hash().into(); println!("Debugging program with hash {}... ", hex::encode(program_hash)); diff --git a/miden/src/cli/prove.rs b/miden/src/cli/prove.rs index a6e0da9b32..eb9b7d8d33 100644 --- a/miden/src/cli/prove.rs +++ b/miden/src/cli/prove.rs @@ -75,7 +75,8 @@ impl ProveCmd { let libraries = Libraries::new(&self.library_paths)?; // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; + let program = + ProgramFile::read(&self.assembly_file)?.compile(&Debug::Off, libraries.libraries)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; diff --git a/miden/src/cli/run.rs b/miden/src/cli/run.rs index 443265b8b4..f123093dd2 100644 --- a/miden/src/cli/run.rs +++ b/miden/src/cli/run.rs @@ -45,7 +45,8 @@ impl RunCmd { let libraries = Libraries::new(&self.library_paths)?; // load program from file and compile - let program = ProgramFile::read(&self.assembly_file, &Debug::Off, libraries.libraries)?; + let program = + ProgramFile::read(&self.assembly_file)?.compile(&Debug::Off, libraries.libraries)?; // load input data from file let input_data = InputFile::read(&self.input_file, &self.assembly_file)?; diff --git a/miden/src/lib.rs b/miden/src/lib.rs index c6158eb773..bb308090cf 100644 --- a/miden/src/lib.rs +++ b/miden/src/lib.rs @@ -4,14 +4,17 @@ // EXPORTS // ================================================================================================ -pub use assembly::{Assembler, AssemblyError, ParsingError}; +pub use assembly::{ + ast::{ModuleAst, ProgramAst}, + Assembler, AssemblyError, ParsingError, +}; pub use processor::{ crypto, execute, execute_iter, utils, AdviceInputs, AdviceProvider, AsmOpInfo, DefaultHost, - ExecutionError, ExecutionTrace, Host, Kernel, MemAdviceProvider, Operation, ProgramInfo, - StackInputs, VmState, VmStateIterator, ZERO, + ExecutionError, ExecutionTrace, Host, Kernel, MemAdviceProvider, Operation, Program, + ProgramInfo, StackInputs, VmState, VmStateIterator, ZERO, }; pub use prover::{ - math, prove, Digest, ExecutionProof, FieldExtension, HashFunction, InputError, Program, - ProvingOptions, StackOutputs, StarkProof, Word, + math, prove, Digest, ExecutionProof, FieldExtension, HashFunction, InputError, ProvingOptions, + StackOutputs, StarkProof, Word, }; pub use verifier::{verify, VerificationError};