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