Skip to main content

miden_testing/
executor.rs

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