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