Skip to main content

miden_testing/
executor.rs

1#[cfg(test)]
2use miden_processor::DefaultHost;
3use miden_processor::advice::AdviceInputs;
4use miden_processor::{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            .with_debugging(true);
72
73        let execution_output =
74            processor.execute(&program, &mut self.host).await.map_err(ExecError::new)?;
75
76        Ok(execution_output)
77    }
78}
79
80#[cfg(test)]
81impl CodeExecutor<DefaultHost> {
82    pub fn with_default_host() -> Self {
83        use miden_core_lib::CoreLibrary;
84        use miden_protocol::ProtocolLib;
85        use miden_protocol::transaction::TransactionKernel;
86        use miden_standards::StandardsLib;
87
88        let mut host = DefaultHost::default();
89
90        let core_lib = CoreLibrary::default();
91        host.load_library(core_lib.mast_forest()).unwrap();
92
93        let standards_lib = StandardsLib::default();
94        host.load_library(standards_lib.mast_forest()).unwrap();
95
96        let protocol_lib = ProtocolLib::default();
97        host.load_library(protocol_lib.mast_forest()).unwrap();
98
99        let kernel_lib = TransactionKernel::library();
100        host.load_library(kernel_lib.mast_forest()).unwrap();
101
102        CodeExecutor::new(host)
103    }
104}