Skip to main content

miden_processor/host/
mod.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::future::Future;
3
4use miden_core::{
5    Word,
6    advice::{AdviceMap, AdviceStack},
7    crypto::merkle::InnerNodeInfo,
8    events::{EventId, EventName},
9};
10use miden_debug_types::{Location, SourceFile, SourceSpan};
11
12use crate::ProcessorState;
13
14pub(super) mod advice;
15
16pub mod debug;
17
18pub mod default;
19
20pub mod handlers;
21use handlers::{EventError, TraceError};
22
23mod mast_forest_store;
24pub use mast_forest_store::{LoadedMastForest, MastForestStore, MemMastForestStore};
25
26// ADVICE MAP MUTATIONS
27// ================================================================================================
28
29/// Any possible way an event can modify the advice provider.
30#[derive(Debug, PartialEq, Eq)]
31pub enum AdviceMutation {
32    ExtendStack { stack: AdviceStack },
33    ExtendMap { other: AdviceMap },
34    ExtendMerkleStore { infos: Vec<InnerNodeInfo> },
35}
36
37impl AdviceMutation {
38    pub fn extend_advice_stack(stack: AdviceStack) -> Self {
39        Self::ExtendStack { stack }
40    }
41
42    pub fn extend_map(other: AdviceMap) -> Self {
43        Self::ExtendMap { other }
44    }
45
46    pub fn extend_merkle_store(infos: impl IntoIterator<Item = InnerNodeInfo>) -> Self {
47        Self::ExtendMerkleStore { infos: Vec::from_iter(infos) }
48    }
49}
50// HOST TRAIT
51// ================================================================================================
52
53/// Defines the host functionality shared by both sync and async execution.
54///
55/// There are two main categories of interactions between the VM and the host:
56/// 1. getting a library's MAST forest,
57/// 2. handling VM events (regular events can mutate the process' advice provider, while trace
58///    events are read-only),
59pub trait BaseHost {
60    // REQUIRED METHODS
61    // --------------------------------------------------------------------------------------------
62
63    /// Returns the [`SourceSpan`] and optional [`SourceFile`] for the provided location.
64    fn get_label_and_source_file(
65        &self,
66        location: &Location,
67    ) -> (SourceSpan, Option<Arc<SourceFile>>);
68
69    // PROVIDED METHODS
70    // --------------------------------------------------------------------------------------------
71
72    /// Returns the [`EventName`] registered for the provided [`EventId`], if any.
73    ///
74    /// Hosts that maintain an event registry can override this method to surface human-readable
75    /// names for diagnostics. The default implementation returns `None`.
76    fn resolve_event(&self, _event_id: EventId) -> Option<&EventName> {
77        None
78    }
79
80    /// Returns the [`EventName`] registered for the provided trace [`EventId`], if any.
81    ///
82    /// Hosts that maintain an trace handler registry can override this method to surface
83    /// human-readable names for diagnostics. The default implementation returns `None`.
84    fn resolve_trace(&self, _trace_id: EventId) -> Option<&EventName> {
85        None
86    }
87}
88
89impl<T: BaseHost + ?Sized> BaseHost for &mut T {
90    fn get_label_and_source_file(
91        &self,
92        location: &Location,
93    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
94        (**self).get_label_and_source_file(location)
95    }
96
97    fn resolve_event(&self, event_id: EventId) -> Option<&EventName> {
98        (**self).resolve_event(event_id)
99    }
100
101    fn resolve_trace(&self, trace_id: EventId) -> Option<&EventName> {
102        (**self).resolve_trace(trace_id)
103    }
104}
105
106/// Defines a synchronous interface by which the VM can interact with the host during execution.
107pub trait SyncHost: BaseHost {
108    /// Returns MAST forest corresponding to the specified digest, or None if the MAST forest for
109    /// this digest could not be found in this host.
110    fn get_mast_forest(&self, node_digest: &Word) -> Option<LoadedMastForest>;
111
112    /// Handles the event emitted from the VM and provides advice mutations to be applied to
113    /// the advice provider.
114    ///
115    /// The event ID is available at the top of the stack (position 0) when this handler is called.
116    /// This allows the handler to access both the event ID and any additional context data that
117    /// may have been pushed onto the stack prior to the emit operation.
118    ///
119    /// ## Implementation notes
120    /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))`
121    /// - Return errors without event names or IDs - the caller will enrich them via
122    ///   [`BaseHost::resolve_event()`]
123    /// - System events are handled by the VM before and don't call this method
124    fn on_event(&mut self, process: &ProcessorState<'_>)
125    -> Result<Vec<AdviceMutation>, EventError>;
126
127    /// Handles a trace event emitted from the VM.
128    ///
129    /// Trace events are optional, read-only events. [`SystemEvent::TraceEvent`] is at stack
130    /// position 0 and the user trace event ID is at position 1 when this handler is called. The
131    /// handler cannot mutate the advice provider. Hosts that do not care about trace events can use
132    /// this default no-op implementation. Hosts are expected to nat raise an error on encountering
133    /// a trace event for which no handler is registered.
134    ///
135    /// Return errors without event names or IDs - the caller will enrich them via
136    /// [`BaseHost::resolve_trace()`].
137    ///
138    /// [`SystemEvent::TraceEvent`]: miden_core::events::SystemEvent::TraceEvent
139    fn on_trace(&mut self, _process: &ProcessorState<'_>) -> Result<(), TraceError> {
140        Ok(())
141    }
142}
143
144/// Defines an async interface by which the VM can interact with the host during execution.
145///
146/// This mirrors the historic async host surface while allowing the sync-first core to depend on
147/// [`BaseHost`].
148pub trait Host: BaseHost {
149    // REQUIRED METHODS
150    // --------------------------------------------------------------------------------------------
151
152    /// Returns MAST forest corresponding to the specified digest, or None if the MAST forest for
153    /// this digest could not be found in this host.
154    fn get_mast_forest(&self, node_digest: &Word)
155    -> impl FutureMaybeSend<Option<LoadedMastForest>>;
156
157    /// Handles the event emitted from the VM and provides advice mutations to be applied to
158    /// the advice provider.
159    ///
160    /// The event ID is available at the top of the stack (position 0) when this handler is called.
161    /// This allows the handler to access both the event ID and any additional context data that
162    /// may have been pushed onto the stack prior to the emit operation.
163    ///
164    /// ## Implementation notes
165    /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))`
166    /// - Return errors without event names or IDs - the caller will enrich them via
167    ///   [`BaseHost::resolve_event()`]
168    /// - System events are handled by the VM before and don't call this method
169    fn on_event(
170        &mut self,
171        process: &ProcessorState<'_>,
172    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>>;
173
174    /// Handles a trace event emitted from the VM.
175    ///
176    /// Trace events are optional, read-only events. [`SystemEvent::TraceEvent`] is at stack
177    /// position 0 and the user trace event ID is at position 1 when this handler is called. The
178    /// handler cannot mutate the advice provider. Hosts that do not care about trace events can use
179    /// this default no-op implementation. Hosts are expected to nat raise an error on encountering
180    /// a trace event for which no handler is registered.
181    ///
182    /// Return errors without event names or IDs - the caller will enrich them via
183    /// [`BaseHost::resolve_trace()`].
184    ///
185    /// [`SystemEvent::TraceEvent`]: miden_core::events::SystemEvent::TraceEvent
186    fn on_trace(
187        &mut self,
188        _process: &ProcessorState<'_>,
189    ) -> impl FutureMaybeSend<Result<(), TraceError>> {
190        async move { Ok(()) }
191    }
192}
193
194impl<T> Host for T
195where
196    T: SyncHost,
197{
198    fn get_mast_forest(
199        &self,
200        node_digest: &Word,
201    ) -> impl FutureMaybeSend<Option<LoadedMastForest>> {
202        let result = SyncHost::get_mast_forest(self, node_digest);
203        async move { result }
204    }
205
206    fn on_event(
207        &mut self,
208        process: &ProcessorState<'_>,
209    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>> {
210        let result = SyncHost::on_event(self, process);
211        async move { result }
212    }
213
214    fn on_trace(
215        &mut self,
216        process: &ProcessorState<'_>,
217    ) -> impl FutureMaybeSend<Result<(), TraceError>> {
218        let result = SyncHost::on_trace(self, process);
219        async move { result }
220    }
221}
222
223/// Alias for a `Future`
224///
225/// Unless the compilation target family is `wasm`, we add `Send` to the required bounds. For
226/// `wasm` compilation targets there is no `Send` bound.
227#[cfg(target_family = "wasm")]
228pub trait FutureMaybeSend<O>: Future<Output = O> {}
229
230#[cfg(target_family = "wasm")]
231impl<T, O> FutureMaybeSend<O> for T where T: Future<Output = O> {}
232
233/// Alias for a `Future`
234///
235/// Unless the compilation target family is `wasm`, we add `Send` to the required bounds. For
236/// `wasm` compilation targets there is no `Send` bound.
237#[cfg(not(target_family = "wasm"))]
238pub trait FutureMaybeSend<O>: Future<Output = O> + Send {}
239
240#[cfg(not(target_family = "wasm"))]
241impl<T, O> FutureMaybeSend<O> for T where T: Future<Output = O> + Send {}