Skip to main content

miden_testing/
executor.rs

1#[cfg(test)]
2use miden_processor::DefaultHost;
3use miden_processor::fast::{ExecutionOutput, FastProcessor};
4use miden_processor::{AdviceInputs, AsyncHost, 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: AsyncHost> 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        // This reverses the stack inputs (even though it doesn't look like it does) because the
68        // fast processor expects the reverse order.
69        //
70        // Once we use the FastProcessor for execution and proving, we can change the way these
71        // inputs are constructed in TransactionKernel::prepare_inputs.
72        let stack_inputs =
73            StackInputs::new(self.stack_inputs.unwrap_or_default().iter().copied().collect())
74                .unwrap();
75
76        let processor = FastProcessor::new_debug(stack_inputs.as_slice(), self.advice_inputs);
77
78        let execution_output =
79            processor.execute(&program, &mut self.host).await.map_err(ExecError::new)?;
80
81        Ok(execution_output)
82    }
83}
84
85#[cfg(test)]
86impl CodeExecutor<DefaultHost> {
87    pub fn with_default_host() -> Self {
88        use miden_core_lib::CoreLibrary;
89        use miden_protocol::ProtocolLib;
90        use miden_protocol::transaction::TransactionKernel;
91        use miden_standards::StandardsLib;
92
93        let mut host = DefaultHost::default();
94
95        let core_lib = CoreLibrary::default();
96        host.load_library(core_lib.mast_forest()).unwrap();
97
98        let standards_lib = StandardsLib::default();
99        host.load_library(standards_lib.mast_forest()).unwrap();
100
101        let protocol_lib = ProtocolLib::default();
102        host.load_library(protocol_lib.mast_forest()).unwrap();
103
104        let kernel_lib = TransactionKernel::library();
105        host.load_library(kernel_lib.mast_forest()).unwrap();
106
107        CodeExecutor::new(host)
108    }
109}