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 {}