use std::{
fs,
io::Write,
path::{Path, PathBuf},
sync::Arc,
};
use miden_assembly::{
Assembler, DefaultSourceManager, Library, Path as LibraryPath, SourceManager,
ast::{Module, ModuleKind},
diagnostics::{Report, WrapErr},
report,
serde::Deserializable,
};
use miden_core::{Felt, field::QuotientMap};
use miden_core_lib::CoreLibrary;
use miden_vm::{ExecutionProof, Program, StackOutputs, Word, serde::SliceReader};
use serde::{Deserialize, Serialize};
use tracing::instrument;
#[derive(Deserialize, Serialize, Debug)]
pub struct OutputFile {
pub stack: Vec<String>,
}
impl OutputFile {
pub fn new(stack_outputs: &StackOutputs) -> Self {
Self {
stack: stack_outputs.iter().map(|&v| v.to_string()).collect::<Vec<String>>(),
}
}
#[instrument(name = "read_output_file",
fields(path = %outputs_path.clone().unwrap_or(program_path.with_extension("outputs")).display()), skip_all)]
pub fn read(outputs_path: &Option<PathBuf>, program_path: &Path) -> Result<Self, String> {
let path = match outputs_path {
Some(path) => path.clone(),
None => program_path.with_extension("outputs"),
};
let outputs_file = fs::read_to_string(&path)
.map_err(|err| format!("Failed to open outputs file `{}` - {}", path.display(), err))?;
let outputs: OutputFile = serde_json::from_str(&outputs_file)
.map_err(|err| format!("Failed to deserialize outputs data - {err}"))?;
Ok(outputs)
}
#[instrument(name = "write_data_to_output_file", fields(path = %path.display()), skip_all)]
pub fn write(stack_outputs: &StackOutputs, path: &PathBuf) -> Result<(), String> {
let file = fs::File::create(path).map_err(|err| {
format!("Failed to create output file `{}` - {}", path.display(), err)
})?;
serde_json::to_writer_pretty(file, &Self::new(stack_outputs))
.map_err(|err| format!("Failed to write output data - {err}"))
}
pub fn stack_outputs(&self) -> Result<StackOutputs, String> {
let stack: Vec<Felt> = self
.stack
.iter()
.map(|v| {
let value = v.parse::<u64>().map_err(|e| e.to_string())?;
Felt::from_canonical_checked(value)
.ok_or_else(|| format!("failed to convert stack input value '{v}' to Felt"))
})
.collect::<Result<_, _>>()
.map_err(|err| format!("Failed to parse stack output as u64 - {err}"))?;
StackOutputs::new(&stack).map_err(|e| format!("Construct stack outputs failed {e}"))
}
}
pub struct ProgramFile<S: SourceManager = DefaultSourceManager> {
ast: Box<Module>,
source_manager: Arc<S>,
}
impl ProgramFile {
pub fn read(path: impl AsRef<Path>) -> Result<Self, Report> {
let source_manager = Arc::new(miden_assembly::DefaultSourceManager::default());
Self::read_with(path, source_manager)
}
}
impl<S> ProgramFile<S>
where
S: SourceManager + 'static,
{
#[instrument(name = "read_program_file", skip(source_manager), fields(path = %path.as_ref().display()))]
pub fn read_with(path: impl AsRef<Path>, source_manager: Arc<S>) -> Result<Self, Report> {
let path = path.as_ref();
let mut parser = Module::parser(ModuleKind::Executable);
let ast = parser
.parse_file(LibraryPath::exec_path(), path, source_manager.clone())
.wrap_err_with(|| format!("Failed to parse program file `{}`", path.display()))?;
Ok(Self { ast, source_manager })
}
#[instrument(name = "compile_program", skip_all)]
pub fn compile<'a, I>(&self, libraries: I) -> Result<Program, Report>
where
I: IntoIterator<Item = &'a Library>,
{
let mut assembler = Assembler::new(self.source_manager.clone());
assembler
.link_dynamic_library(CoreLibrary::default())
.wrap_err("Failed to load core library")?;
for library in libraries {
assembler.link_dynamic_library(library).wrap_err("Failed to load libraries")?;
}
let program: Program = assembler
.assemble_program(self.ast.as_ref())
.wrap_err("Failed to compile program")?;
Ok(program)
}
pub fn source_manager(&self) -> &Arc<S> {
&self.source_manager
}
pub fn ast(&self) -> &Module {
&self.ast
}
}
pub struct ProofFile;
impl ProofFile {
#[instrument(name = "read_proof_file",
fields(path = %proof_path.clone().unwrap_or(program_path.with_extension("proof")).display()), skip_all)]
pub fn read(
proof_path: &Option<PathBuf>,
program_path: &Path,
) -> Result<ExecutionProof, String> {
let path = match proof_path {
Some(path) => path.clone(),
None => program_path.with_extension("proof"),
};
let file = fs::read(&path)
.map_err(|err| format!("Failed to open proof file `{}` - {}", path.display(), err))?;
ExecutionProof::from_bytes(&file)
.map_err(|err| format!("Failed to decode proof data - {err}"))
}
#[instrument(name = "write_data_to_proof_file",
fields(
path = %proof_path.clone().unwrap_or(program_path.with_extension("proof")).display(),
size = format!("{} KB", proof.to_bytes().len() / 1024)), skip_all)]
pub fn write(
proof: ExecutionProof,
proof_path: &Option<PathBuf>,
program_path: &Path,
) -> Result<(), String> {
let path = match proof_path {
Some(path) => path.clone(),
None => program_path.with_extension("proof"),
};
let mut file = fs::File::create(&path)
.map_err(|err| format!("Failed to create proof file `{}` - {}", path.display(), err))?;
let proof_bytes = proof.to_bytes();
file.write_all(&proof_bytes)
.map_err(|err| format!("Failed to write proof file `{}` - {}", path.display(), err))?;
Ok(())
}
}
pub struct ProgramHash;
impl ProgramHash {
#[instrument(name = "read_program_hash", skip_all)]
pub fn read(hash_hex_string: &str) -> Result<Word, String> {
let program_hash_bytes = hex::decode(hash_hex_string)
.map_err(|err| format!("Failed to convert program hash to bytes {err}"))?;
let mut program_hash_slice = SliceReader::new(&program_hash_bytes);
let program_hash = Word::read_from(&mut program_hash_slice)
.map_err(|err| format!("Failed to deserialize program hash from bytes - {err}"))?;
Ok(program_hash)
}
}
pub struct Libraries {
pub libraries: Vec<Library>,
}
impl Libraries {
#[instrument(name = "read_library_files", skip_all)]
pub fn new<P, I>(paths: I) -> Result<Self, Report>
where
P: AsRef<Path>,
I: IntoIterator<Item = P>,
{
let mut libraries = Vec::new();
for path in paths {
let path_str = path.as_ref().to_string_lossy().into_owned();
let library = Library::deserialize_from_file(path).map_err(|err| {
report!("Failed to read library from file `{}`: {}", path_str, err)
})?;
libraries.push(library);
}
Ok(Self { libraries })
}
}