Skip to main content

miden_processor/host/
mod.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::future::Future;
3
4use miden_core::{
5    Felt, 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 { map: AdviceMap },
34    ExtendMerkleStore { inner_nodes: Vec<InnerNodeInfo> },
35}
36
37impl AdviceMutation {
38    pub fn extend_advice_stack(stack: AdviceStack) -> Self {
39        Self::ExtendStack { stack }
40    }
41
42    /// Extends the advice stack with `elements`, ordered from the top of the stack down.
43    ///
44    /// The typed [`AdviceMutation::extend_advice_stack`] is the one to reach for when the caller
45    /// already holds an [`AdviceStack`], or needs its element/word/dword layout helpers. This one
46    /// covers the common case of a host reply that is just a handful of field elements, which
47    /// would otherwise have to build an [`AdviceStack`] only to hand it straight over.
48    pub fn extend_advice_stack_with(elements: impl IntoIterator<Item = Felt>) -> Self {
49        Self::ExtendStack { stack: elements.into_iter().collect() }
50    }
51
52    pub fn extend_map(map: AdviceMap) -> Self {
53        Self::ExtendMap { map }
54    }
55
56    pub fn extend_merkle_store(inner_nodes: impl IntoIterator<Item = InnerNodeInfo>) -> Self {
57        Self::ExtendMerkleStore { inner_nodes: Vec::from_iter(inner_nodes) }
58    }
59}
60// HOST TRAIT
61// ================================================================================================
62
63/// Defines the host functionality shared by both sync and async execution.
64///
65/// There are two main categories of interactions between the VM and the host:
66/// 1. getting a library's MAST forest,
67/// 2. handling VM events (regular events can mutate the process' advice provider, while trace
68///    events are read-only),
69pub trait BaseHost {
70    // REQUIRED METHODS
71    // --------------------------------------------------------------------------------------------
72
73    /// Returns the [`SourceSpan`] and optional [`SourceFile`] for the provided location.
74    fn get_label_and_source_file(
75        &self,
76        location: &Location,
77    ) -> (SourceSpan, Option<Arc<SourceFile>>);
78
79    // PROVIDED METHODS
80    // --------------------------------------------------------------------------------------------
81
82    /// Returns the [`EventName`] registered for the provided [`EventId`], if any.
83    ///
84    /// Hosts that maintain an event registry can override this method to surface human-readable
85    /// names for diagnostics. The default implementation returns `None`.
86    fn resolve_event(&self, _event_id: EventId) -> Option<&EventName> {
87        None
88    }
89
90    /// Returns the [`EventName`] registered for the provided trace [`EventId`], if any.
91    ///
92    /// Hosts that maintain an trace handler registry can override this method to surface
93    /// human-readable names for diagnostics. The default implementation returns `None`.
94    fn resolve_trace(&self, _trace_id: EventId) -> Option<&EventName> {
95        None
96    }
97}
98
99impl<T: BaseHost + ?Sized> BaseHost for &mut T {
100    fn get_label_and_source_file(
101        &self,
102        location: &Location,
103    ) -> (SourceSpan, Option<Arc<SourceFile>>) {
104        (**self).get_label_and_source_file(location)
105    }
106
107    fn resolve_event(&self, event_id: EventId) -> Option<&EventName> {
108        (**self).resolve_event(event_id)
109    }
110
111    fn resolve_trace(&self, trace_id: EventId) -> Option<&EventName> {
112        (**self).resolve_trace(trace_id)
113    }
114}
115
116/// Defines a synchronous interface by which the VM can interact with the host during execution.
117pub trait SyncHost: BaseHost {
118    /// Returns MAST forest corresponding to the specified digest, or None if the MAST forest for
119    /// this digest could not be found in this host.
120    fn get_mast_forest(&self, node_digest: &Word) -> Option<LoadedMastForest>;
121
122    /// Handles the event emitted from the VM and provides advice mutations to be applied to
123    /// the advice provider.
124    ///
125    /// The event ID is available at the top of the stack (position 0) when this handler is called.
126    /// This allows the handler to access both the event ID and any additional context data that
127    /// may have been pushed onto the stack prior to the emit operation.
128    ///
129    /// ## Implementation notes
130    /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))`
131    /// - Return errors without event names or IDs - the caller will enrich them via
132    ///   [`BaseHost::resolve_event()`]
133    /// - System events are handled by the VM before and don't call this method
134    fn on_event(&mut self, process: &ProcessorState<'_>)
135    -> Result<Vec<AdviceMutation>, EventError>;
136
137    /// Handles a trace event emitted from the VM.
138    ///
139    /// Trace events are optional, read-only events. [`SystemEvent::TraceEvent`] is at stack
140    /// position 0 and the user trace event ID is at position 1 when this handler is called. The
141    /// handler cannot mutate the advice provider. Hosts that do not care about trace events can use
142    /// this default no-op implementation. Hosts are expected not to raise an error on encountering
143    /// a trace event for which no handler is registered.
144    ///
145    /// Return errors without event names or IDs - the caller will enrich them via
146    /// [`BaseHost::resolve_trace()`].
147    ///
148    /// [`SystemEvent::TraceEvent`]: miden_core::events::SystemEvent::TraceEvent
149    fn on_trace(&mut self, _process: &ProcessorState<'_>) -> Result<(), TraceError> {
150        Ok(())
151    }
152}
153
154/// Defines an async interface by which the VM can interact with the host during execution.
155///
156/// This mirrors the historic async host surface while allowing the sync-first core to depend on
157/// [`BaseHost`].
158pub trait Host: BaseHost {
159    // REQUIRED METHODS
160    // --------------------------------------------------------------------------------------------
161
162    /// Returns MAST forest corresponding to the specified digest, or None if the MAST forest for
163    /// this digest could not be found in this host.
164    fn get_mast_forest(&self, node_digest: &Word)
165    -> impl FutureMaybeSend<Option<LoadedMastForest>>;
166
167    /// Handles the event emitted from the VM and provides advice mutations to be applied to
168    /// the advice provider.
169    ///
170    /// The event ID is available at the top of the stack (position 0) when this handler is called.
171    /// This allows the handler to access both the event ID and any additional context data that
172    /// may have been pushed onto the stack prior to the emit operation.
173    ///
174    /// ## Implementation notes
175    /// - Extract the event ID via `EventId::from_felt(process.get_stack_item(0))`
176    /// - Return errors without event names or IDs - the caller will enrich them via
177    ///   [`BaseHost::resolve_event()`]
178    /// - System events are handled by the VM before and don't call this method
179    fn on_event(
180        &mut self,
181        process: &ProcessorState<'_>,
182    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>>;
183
184    /// Handles a trace event emitted from the VM.
185    ///
186    /// Trace events are optional, read-only events. [`SystemEvent::TraceEvent`] is at stack
187    /// position 0 and the user trace event ID is at position 1 when this handler is called. The
188    /// handler cannot mutate the advice provider. Hosts that do not care about trace events can use
189    /// this default no-op implementation. Hosts are expected not to raise an error on encountering
190    /// a trace event for which no handler is registered.
191    ///
192    /// Return errors without event names or IDs - the caller will enrich them via
193    /// [`BaseHost::resolve_trace()`].
194    ///
195    /// [`SystemEvent::TraceEvent`]: miden_core::events::SystemEvent::TraceEvent
196    fn on_trace(
197        &mut self,
198        _process: &ProcessorState<'_>,
199    ) -> impl FutureMaybeSend<Result<(), TraceError>> {
200        async move { Ok(()) }
201    }
202}
203
204impl<T> Host for T
205where
206    T: SyncHost,
207{
208    fn get_mast_forest(
209        &self,
210        node_digest: &Word,
211    ) -> impl FutureMaybeSend<Option<LoadedMastForest>> {
212        let result = SyncHost::get_mast_forest(self, node_digest);
213        async move { result }
214    }
215
216    fn on_event(
217        &mut self,
218        process: &ProcessorState<'_>,
219    ) -> impl FutureMaybeSend<Result<Vec<AdviceMutation>, EventError>> {
220        let result = SyncHost::on_event(self, process);
221        async move { result }
222    }
223
224    fn on_trace(
225        &mut self,
226        process: &ProcessorState<'_>,
227    ) -> impl FutureMaybeSend<Result<(), TraceError>> {
228        let result = SyncHost::on_trace(self, process);
229        async move { result }
230    }
231}
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(target_family = "wasm")]
238pub trait FutureMaybeSend<O>: Future<Output = O> {}
239
240#[cfg(target_family = "wasm")]
241impl<T, O> FutureMaybeSend<O> for T where T: Future<Output = O> {}
242
243/// Alias for a `Future`
244///
245/// Unless the compilation target family is `wasm`, we add `Send` to the required bounds. For
246/// `wasm` compilation targets there is no `Send` bound.
247#[cfg(not(target_family = "wasm"))]
248pub trait FutureMaybeSend<O>: Future<Output = O> + Send {}
249
250#[cfg(not(target_family = "wasm"))]
251impl<T, O> FutureMaybeSend<O> for T where T: Future<Output = O> + Send {}
252
253#[cfg(test)]
254mod tests {
255    use super::{AdviceMutation, AdviceStack, Felt};
256
257    /// The iterator helper must be indistinguishable from building the stack by hand, so that a
258    /// handler can switch to it without changing what the VM sees.
259    ///
260    /// Driven from a lazy `Map` rather than a collection, since taking any `IntoIterator` is the
261    /// point of the helper.
262    #[test]
263    fn extend_advice_stack_with_matches_the_typed_helper() {
264        let mut stack = AdviceStack::new();
265        stack.append_elements((1..=3u32).map(Felt::from_u32));
266
267        assert_eq!(
268            AdviceMutation::extend_advice_stack_with((1..=3u32).map(Felt::from_u32)),
269            AdviceMutation::extend_advice_stack(stack)
270        );
271    }
272}