Skip to main content

miden_testing/
executor.rs

1#[cfg(test)]
2use miden_processor::DefaultHost;
3use miden_processor::advice::AdviceInputs;
4use miden_processor::{
5    ExecutionError,
6    ExecutionOptions,
7    ExecutionOutput,
8    FastProcessor,
9    Host,
10    Program,
11    StackInputs,
12};
13#[cfg(test)]
14use miden_protocol::assembly::Assembler;
15
16use crate::ExecError;
17
18// CODE EXECUTOR
19// ================================================================================================
20
21/// Helper for executing arbitrary code within arbitrary hosts.
22pub(crate) struct CodeExecutor<H> {
23    host: H,
24    stack_inputs: Option<StackInputs>,
25    advice_inputs: AdviceInputs,
26    execution_options: Option<ExecutionOptions>,
27}
28
29impl<H: Host> CodeExecutor<H> {
30    // CONSTRUCTOR
31    // --------------------------------------------------------------------------------------------
32    pub(crate) fn new(host: H) -> Self {
33        Self {
34            host,
35            stack_inputs: None,
36            advice_inputs: AdviceInputs::default(),
37            execution_options: None,
38        }
39    }
40
41    pub fn extend_advice_inputs(mut self, advice_inputs: AdviceInputs) -> Self {
42        self.advice_inputs.extend(advice_inputs);
43        self
44    }
45
46    pub fn stack_inputs(mut self, stack_inputs: StackInputs) -> Self {
47        self.stack_inputs = Some(stack_inputs);
48        self
49    }
50
51    /// Overrides the [`ExecutionOptions`] used to run the program (e.g. to cap `max_cycles`).
52    #[cfg(test)]
53    pub fn execution_options(mut self, options: ExecutionOptions) -> Self {
54        self.execution_options = Some(options);
55        self
56    }
57
58    /// Compiles and runs the desired code in the host and returns the [`Process`] state.
59    #[cfg(test)]
60    pub async fn run(self, code: &str) -> Result<ExecutionOutput, ExecError> {
61        use alloc::borrow::ToOwned;
62        use alloc::sync::Arc;
63
64        use miden_protocol::assembly::debuginfo::{SourceLanguage, Uri};
65        use miden_protocol::assembly::{DefaultSourceManager, SourceManagerSync};
66        use miden_standards::code_builder::CodeBuilder;
67
68        let source_manager: Arc<dyn SourceManagerSync> = Arc::new(DefaultSourceManager::default());
69        let assembler: Assembler = CodeBuilder::with_kernel_library(source_manager.clone()).into();
70
71        // Virtual file name should be unique.
72        let virtual_source_file =
73            source_manager.load(SourceLanguage::Masm, Uri::new("_user_code"), code.to_owned());
74        let program = assembler.assemble_program(virtual_source_file).unwrap();
75
76        self.execute_program(program).await
77    }
78
79    /// Executes the provided [`Program`] and returns the [`Process`] state.
80    ///
81    /// To improve the error message quality, convert the returned [`ExecutionError`] into a
82    /// [`Report`](miden_protocol::assembly::diagnostics::Report).
83    pub async fn execute_program(mut self, program: Program) -> Result<ExecutionOutput, ExecError> {
84        let stack_inputs = self.stack_inputs.unwrap_or_default();
85
86        let processor = FastProcessor::new(stack_inputs)
87            .with_advice(self.advice_inputs)
88            .map_err(ExecutionError::advice_error_no_context)
89            .map_err(ExecError::new)?
90            .with_options(self.execution_options.unwrap_or_default())
91            .map_err(ExecutionError::advice_error_no_context)
92            .map_err(ExecError::new)?
93            .with_debugging(true);
94
95        let execution_output =
96            processor.execute(&program, &mut self.host).await.map_err(ExecError::new)?;
97
98        Ok(execution_output)
99    }
100}
101
102#[cfg(test)]
103impl CodeExecutor<DefaultHost> {
104    pub fn with_default_host() -> Self {
105        use miden_core_lib::CoreLibrary;
106        use miden_protocol::ProtocolLib;
107        use miden_protocol::transaction::TransactionKernel;
108        use miden_standards::StandardsLib;
109
110        let mut host = DefaultHost::default();
111
112        let core_lib = CoreLibrary::default();
113        host.load_library(core_lib.mast_forest()).unwrap();
114
115        let standards_lib = StandardsLib::default();
116        host.load_library(standards_lib.mast_forest()).unwrap();
117
118        let protocol_lib = ProtocolLib::default();
119        host.load_library(protocol_lib.mast_forest()).unwrap();
120
121        let kernel_lib = TransactionKernel::library();
122        host.load_library(kernel_lib.mast_forest()).unwrap();
123
124        CodeExecutor::new(host)
125    }
126}