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}