Skip to main content

miden_processor/
lib.rs

1#![no_std]
2
3#[macro_use]
4extern crate alloc;
5
6#[cfg(feature = "std")]
7extern crate std;
8
9use alloc::vec::Vec;
10use core::{
11    fmt::{self, Display, LowerHex},
12    ops::ControlFlow,
13};
14
15mod continuation_stack;
16mod debug;
17mod errors;
18mod execution;
19mod execution_options;
20mod fast;
21mod host;
22mod processor;
23mod tracer;
24
25use crate::{
26    advice::{AdviceInputs, AdviceProvider},
27    errors::MapExecErr,
28    field::{PrimeCharacteristicRing, PrimeField64},
29    processor::{Processor, SystemInterface},
30    trace::{ExecutionTrace, RowIndex},
31};
32
33#[cfg(any(test, feature = "testing"))]
34mod test_utils;
35#[cfg(any(test, feature = "testing"))]
36pub use test_utils::{ProcessorStateSnapshot, TestHost, TraceCollector};
37
38#[cfg(test)]
39mod tests;
40
41// RE-EXPORTS
42// ================================================================================================
43
44pub use errors::{ExecutionError, MemoryError};
45pub use execution_options::{ExecutionOptions, ExecutionOptionsError};
46pub use fast::{BreakReason, ExecutionOutput, FastProcessor, ResumeContext};
47pub use host::{
48    FutureMaybeSend, Host, MastForestStore, MemMastForestStore,
49    debug::DefaultDebugHandler,
50    default::{DefaultHost, HostLibrary},
51    handlers::{DebugError, DebugHandler, TraceError},
52};
53pub use miden_core::{
54    EMPTY_WORD, Felt, ONE, WORD_SIZE, Word, ZERO, crypto, field, mast, precompile,
55    program::{
56        InputError, Kernel, MIN_STACK_DEPTH, Program, ProgramInfo, StackInputs, StackOutputs,
57    },
58    serde, utils,
59};
60
61pub mod advice {
62    pub use miden_core::advice::{AdviceInputs, AdviceMap, AdviceStackBuilder};
63
64    pub use super::host::{
65        AdviceMutation,
66        advice::{AdviceError, AdviceProvider},
67    };
68}
69
70pub mod event {
71    pub use miden_core::events::*;
72
73    pub use crate::host::handlers::{
74        EventError, EventHandler, EventHandlerRegistry, NoopEventHandler,
75    };
76}
77
78pub mod operation {
79    pub use miden_core::operations::*;
80
81    pub use crate::errors::OperationError;
82}
83
84pub mod trace;
85
86// EXECUTORS
87// ================================================================================================
88
89/// Returns an execution trace resulting from executing the provided program against the provided
90/// inputs.
91///
92/// This is an async function that works on all platforms including wasm32.
93///
94/// The `host` parameter is used to provide the external environment to the program being executed,
95/// such as access to the advice provider and libraries that the program depends on.
96///
97/// # Errors
98/// Returns an error if program execution fails for any reason.
99#[tracing::instrument("execute_program", skip_all)]
100pub async fn execute(
101    program: &Program,
102    stack_inputs: StackInputs,
103    advice_inputs: AdviceInputs,
104    host: &mut impl Host,
105    options: ExecutionOptions,
106) -> Result<ExecutionTrace, ExecutionError> {
107    let processor = FastProcessor::new_with_options(stack_inputs, advice_inputs, options);
108    let (execution_output, trace_generation_context) =
109        processor.execute_for_trace(program, host).await?;
110
111    let trace = trace::build_trace(execution_output, trace_generation_context, program.to_info());
112
113    assert_eq!(&program.hash(), trace.program_hash(), "inconsistent program hash");
114    Ok(trace)
115}
116
117/// Synchronous wrapper for the async `execute()` function.
118///
119/// This method is only available on non-wasm32 targets. On wasm32, use the async `execute()`
120/// method directly since wasm32 runs in the browser's event loop.
121///
122/// # Panics
123/// Panics if called from within an existing Tokio runtime. Use the async `execute()` method
124/// instead in async contexts.
125#[cfg(not(target_arch = "wasm32"))]
126#[tracing::instrument("execute_program_sync", skip_all)]
127pub fn execute_sync(
128    program: &Program,
129    stack_inputs: StackInputs,
130    advice_inputs: AdviceInputs,
131    host: &mut impl Host,
132    options: ExecutionOptions,
133) -> Result<ExecutionTrace, ExecutionError> {
134    match tokio::runtime::Handle::try_current() {
135        Ok(_handle) => {
136            // We're already inside a Tokio runtime - this is not supported because we cannot
137            // safely create a nested runtime or move the non-Send host reference to another thread
138            panic!(
139                "Cannot call execute_sync from within a Tokio runtime. \
140                 Use the async execute() method instead."
141            )
142        },
143        Err(_) => {
144            // No runtime exists - create one and use it
145            let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
146            rt.block_on(execute(program, stack_inputs, advice_inputs, host, options))
147        },
148    }
149}
150
151// PROCESSOR STATE
152// ===============================================================================================
153
154/// A view into the current state of the processor.
155///
156/// This struct provides read access to the processor's state, including the stack, memory,
157/// advice provider, and execution context information.
158#[derive(Debug)]
159pub struct ProcessorState<'a> {
160    processor: &'a mut FastProcessor,
161}
162
163impl<'a> ProcessorState<'a> {
164    /// Returns a reference to the advice provider.
165    #[inline(always)]
166    pub fn advice_provider(&self) -> &AdviceProvider {
167        self.processor.advice_provider()
168    }
169
170    /// Returns a mutable reference to the advice provider.
171    #[inline(always)]
172    pub fn advice_provider_mut(&mut self) -> &mut AdviceProvider {
173        self.processor.advice_provider_mut()
174    }
175
176    /// Returns the current clock cycle of a process.
177    #[inline(always)]
178    pub fn clock(&self) -> RowIndex {
179        self.processor.clock()
180    }
181
182    /// Returns the current execution context ID.
183    #[inline(always)]
184    pub fn ctx(&self) -> ContextId {
185        self.processor.ctx()
186    }
187
188    /// Returns the value located at the specified position on the stack at the current clock cycle.
189    ///
190    /// This method can access elements beyond the top 16 positions by using the overflow table.
191    #[inline(always)]
192    pub fn get_stack_item(&self, pos: usize) -> Felt {
193        self.processor.stack_get(pos)
194    }
195
196    /// Returns a word starting at the specified element index on the stack.
197    ///
198    /// The word is formed by taking 4 consecutive elements starting from the specified index.
199    /// For example, start_idx=0 creates a word from stack elements 0-3, start_idx=1 creates
200    /// a word from elements 1-4, etc.
201    ///
202    /// Stack element N will be at position 0 of the word, N+1 at position 1, N+2 at position 2,
203    /// and N+3 at position 3. `word[0]` corresponds to the top of the stack.
204    ///
205    /// This method can access elements beyond the top 16 positions by using the overflow table.
206    /// Creating a word does not change the state of the stack.
207    #[inline(always)]
208    pub fn get_stack_word(&self, start_idx: usize) -> Word {
209        self.processor.stack_get_word(start_idx)
210    }
211
212    /// Returns stack state at the current clock cycle. This includes the top 16 items of the
213    /// stack + overflow entries.
214    #[inline(always)]
215    pub fn get_stack_state(&self) -> Vec<Felt> {
216        self.processor.stack().iter().rev().copied().collect()
217    }
218
219    /// Returns the element located at the specified context/address, or None if the address hasn't
220    /// been accessed previously.
221    #[inline(always)]
222    pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
223        self.processor.memory().read_element_impl(ctx, addr)
224    }
225
226    /// Returns the batch of elements starting at the specified context/address.
227    ///
228    /// # Errors
229    /// - If the address is not word aligned.
230    #[inline(always)]
231    pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, MemoryError> {
232        self.processor.memory().read_word_impl(ctx, addr)
233    }
234
235    /// Reads (start_addr, end_addr) tuple from the specified elements of the operand stack (
236    /// without modifying the state of the stack), and verifies that memory range is valid.
237    pub fn get_mem_addr_range(
238        &self,
239        start_idx: usize,
240        end_idx: usize,
241    ) -> Result<core::ops::Range<u32>, MemoryError> {
242        let start_addr = self.get_stack_item(start_idx).as_canonical_u64();
243        let end_addr = self.get_stack_item(end_idx).as_canonical_u64();
244
245        if start_addr > u32::MAX as u64 {
246            return Err(MemoryError::AddressOutOfBounds { addr: start_addr });
247        }
248        if end_addr > u32::MAX as u64 {
249            return Err(MemoryError::AddressOutOfBounds { addr: end_addr });
250        }
251
252        if start_addr > end_addr {
253            return Err(MemoryError::InvalidMemoryRange { start_addr, end_addr });
254        }
255
256        Ok(start_addr as u32..end_addr as u32)
257    }
258
259    /// Returns the entire memory state for the specified execution context at the current clock
260    /// cycle.
261    ///
262    /// The state is returned as a vector of (address, value) tuples, and includes addresses which
263    /// have been accessed at least once.
264    #[inline(always)]
265    pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(MemoryAddress, Felt)> {
266        self.processor.memory().get_memory_state(ctx)
267    }
268}
269
270// STOPPER
271// ===============================================================================================
272
273/// A trait for types that determine whether execution should be stopped at a given point.
274pub trait Stopper {
275    type Processor;
276
277    /// Determines whether execution should be stopped.
278    ///
279    /// The `continuation_after_stop` is provided in cases where simply resuming execution from the
280    /// top of the continuation stack is not sufficient to continue execution correctly. For
281    /// example, when stopping execution in the middle of a basic block, we need to provide a
282    /// `ResumeBasicBlock` continuation to ensure that execution resumes at the correct operation
283    /// within the basic block (i.e. the operation right after the one that was last executed before
284    /// being stopped). No continuation is provided in case of error, since it is expected that
285    /// execution will not be resumed.
286    fn should_stop(
287        &self,
288        processor: &Self::Processor,
289        continuation_after_stop: impl FnOnce() -> Option<continuation_stack::Continuation>,
290    ) -> ControlFlow<BreakReason>;
291}
292
293// EXECUTION CONTEXT
294// ================================================================================================
295
296/// Represents the ID of an execution context
297#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
298pub struct ContextId(u32);
299
300impl ContextId {
301    /// Returns the root context ID
302    pub fn root() -> Self {
303        Self(0)
304    }
305
306    /// Returns true if the context ID represents the root context
307    pub fn is_root(&self) -> bool {
308        self.0 == 0
309    }
310}
311
312impl From<RowIndex> for ContextId {
313    fn from(value: RowIndex) -> Self {
314        Self(value.as_u32())
315    }
316}
317
318impl From<u32> for ContextId {
319    fn from(value: u32) -> Self {
320        Self(value)
321    }
322}
323
324impl From<ContextId> for u32 {
325    fn from(context_id: ContextId) -> Self {
326        context_id.0
327    }
328}
329
330impl From<ContextId> for u64 {
331    fn from(context_id: ContextId) -> Self {
332        context_id.0.into()
333    }
334}
335
336impl From<ContextId> for Felt {
337    fn from(context_id: ContextId) -> Self {
338        Felt::from_u32(context_id.0)
339    }
340}
341
342impl Display for ContextId {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        write!(f, "{}", self.0)
345    }
346}
347
348// MEMORY ADDRESS
349// ================================================================================================
350
351#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
352pub struct MemoryAddress(u32);
353
354impl From<u32> for MemoryAddress {
355    fn from(addr: u32) -> Self {
356        MemoryAddress(addr)
357    }
358}
359
360impl From<MemoryAddress> for u32 {
361    fn from(value: MemoryAddress) -> Self {
362        value.0
363    }
364}
365
366impl Display for MemoryAddress {
367    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
368        Display::fmt(&self.0, f)
369    }
370}
371
372impl LowerHex for MemoryAddress {
373    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
374        LowerHex::fmt(&self.0, f)
375    }
376}
377
378impl core::ops::Add<MemoryAddress> for MemoryAddress {
379    type Output = Self;
380
381    fn add(self, rhs: MemoryAddress) -> Self::Output {
382        MemoryAddress(self.0 + rhs.0)
383    }
384}
385
386impl core::ops::Add<u32> for MemoryAddress {
387    type Output = Self;
388
389    fn add(self, rhs: u32) -> Self::Output {
390        MemoryAddress(self.0 + rhs)
391    }
392}