Skip to main content

cairo_program_runner_lib/
utils.rs

1use std::{
2    any::Any,
3    io,
4    path::{Path, PathBuf},
5};
6
7use cairo_vm::{
8    cairo_run::CairoRunConfig,
9    types::{
10        errors::program_errors::ProgramError, layout::CairoLayoutParams, layout_name::LayoutName,
11        program::Program,
12    },
13    vm::runners::cairo_runner::CairoRunner,
14    Felt252,
15};
16
17use crate::types::RunMode;
18
19#[derive(Debug)]
20pub enum ProgramInput {
21    Path(PathBuf),
22    Json(String),
23    Value(Box<dyn Any>),
24}
25
26impl ProgramInput {
27    pub fn from_value<T: Any>(value: T) -> Self {
28        Self::Value(Box::new(value))
29    }
30}
31
32/// Loads a Cairo program from a file.
33///
34/// This function reads a Cairo program from the specified file path, expecting
35/// an entry point named `"main"`. It returns a `Program` instance on success or a
36/// `ProgramError` if the file cannot be read or parsed as a valid Cairo program.
37///
38/// # Arguments
39///
40/// * `program` - A reference to the file path containing the Cairo program.
41///
42/// # Errors
43///
44/// Returns a `ProgramError` if the program file cannot be read or is invalid.
45pub fn get_program(program: &Path) -> Result<Program, ProgramError> {
46    Program::from_file(program, Some("main"))
47}
48
49/// Wraps the input for a Cairo program.
50///
51/// This function checks if an input file is provided. If so, it returns the path wrapped
52/// as `ProgramInput::Path`. If no input file is provided, it returns `Ok(None)`.
53///
54/// # Arguments
55///
56/// * `program_input` - An optional file path to the input file.
57///
58/// # Errors
59///
60/// Returns an `io::Error` only for API consistency.
61pub fn get_program_input_from_path(
62    program_input: &Option<PathBuf>,
63) -> io::Result<Option<ProgramInput>> {
64    Ok(program_input
65        .as_ref()
66        .map(|input_path| ProgramInput::Path(input_path.to_path_buf())))
67}
68
69/// Creates a Cairo run configuration based on the provided parameters.
70///
71/// Depending on the `proof_mode` flag, this function creates a run configuration
72/// for either proof or validation. If a dynamic parameters file is provided,
73/// the layout must be `LayoutName::dynamic`, and the dynamic layout parameters are read
74/// from the file.
75///
76/// # Arguments
77///
78/// * `dynamic_params_file` - An optional file path containing dynamic layout parameters.
79/// * `layout` - The layout name to use for the run configuration.
80/// * `proof_mode` - A boolean flag indicating whether to generate a proof (true) or run in
81///   validation mode (false).
82/// * `disable_trace_padding` - A boolean flag to disable trace padding (used in proof mode).
83/// * `allow_missing_builtins` - A boolean flag to allow missing built-ins (used in validation
84///   mode).
85/// * `relocate_mem` - A boolean flag indicating whether to relocate memory in the VM (used in proof
86///   mode).
87///
88/// # Returns
89///
90/// Returns a `CairoRunConfig` instance configured according to the provided parameters.
91///
92/// # Errors
93///
94/// Returns an `io::Error` if there is an issue reading the dynamic layout parameters file.
95///
96/// # Panics
97///
98/// Panics if `dynamic_params_file` is provided but `layout` is not `LayoutName::dynamic`.
99pub fn get_cairo_run_config(
100    dynamic_params_file: &Option<PathBuf>,
101    layout: LayoutName,
102    proof_mode: bool,
103    disable_trace_padding: bool,
104    allow_missing_builtins: bool,
105    relocate_mem: bool,
106) -> std::io::Result<CairoRunConfig<'static>> {
107    let dynamic_layout_params = match dynamic_params_file {
108        Some(file) => {
109            assert!(
110                layout == LayoutName::dynamic,
111                "dynamic_params_file should not be provided for layout {layout}."
112            );
113            Some(CairoLayoutParams::from_file(file)?)
114        }
115        None => None,
116    };
117
118    Ok(if proof_mode {
119        RunMode::Proof {
120            layout,
121            dynamic_layout_params,
122            disable_trace_padding,
123            relocate_mem,
124        }
125        .create_config()
126    } else {
127        RunMode::Validation {
128            layout,
129            allow_missing_builtins,
130        }
131        .create_config()
132    })
133}
134
135/// Write the program output to the specified output path as Felt252 values.
136pub fn write_output_to_file(runner: &mut CairoRunner, output_path: PathBuf) -> anyhow::Result<()> {
137    let mut output_buffer = String::new();
138    runner.vm.write_output(&mut output_buffer)?;
139    let output_lines = output_buffer
140        .lines()
141        .map(|line| {
142            Felt252::from_dec_str(line)
143                .map_err(|_| anyhow::anyhow!("Failed to parse output line as Felt decimal: {line}"))
144        })
145        .collect::<Result<Vec<Felt252>, anyhow::Error>>()?;
146    std::fs::write(&output_path, sonic_rs::to_string_pretty(&output_lines)?)?;
147    Ok(())
148}