Skip to main content

miden_core/events/
mod.rs

1use alloc::{borrow::Cow, string::String};
2use core::fmt::{Display, Formatter};
3
4use miden_crypto::{
5    field::PrimeField64,
6    utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::{Felt, utils::hash_string_to_word};
12
13mod sys_events;
14pub use sys_events::SystemEvent;
15
16// EVENT ID
17// ================================================================================================
18
19/// A type-safe event identifier that semantically represents a [`Felt`] value.
20///
21/// Event IDs are used to identify events that can be emitted by the VM or handled by the host.
22/// This newtype provides type safety and ensures that event IDs are not accidentally confused
23/// with other field element values.
24///
25/// Internally stored as `u64` rather than [`Felt`] to enable `const` construction.
26///
27/// [`EventId`] contains only the identifier. For events with human-readable names,
28/// use [`EventName`] instead.
29///
30/// Event IDs are derived from event names using blake3 hashing.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33#[cfg_attr(feature = "serde", serde(transparent))]
34#[cfg_attr(
35    all(feature = "arbitrary", test),
36    miden_test_serde_macros::serde_test(binary_serde(true))
37)]
38pub struct EventId(u64);
39
40impl EventId {
41    /// Computes the canonical event identifier for the given `name`.
42    ///
43    /// This function provides a stable, deterministic mapping from human-readable event names
44    /// to field elements that can be used as event identifiers in the VM. The mapping works by:
45    /// 1. Computing the BLAKE3 hash of the event name (produces 32 bytes)
46    /// 2. Taking the first 8 bytes of the hash
47    /// 3. Interpreting these bytes as a little-endian u64
48    /// 4. Reducing modulo the field prime to produce a valid field element
49    ///
50    /// Note that this is the same procedure performed by [`hash_string_to_word`], where we take
51    /// the first element of the resulting [`Word`](crate::Word).
52    ///
53    /// This ensures that identical event names always produce the same event ID, while
54    /// providing good distribution properties to minimize collisions between different names.
55    pub fn from_name(name: impl AsRef<str>) -> Self {
56        let digest_word = hash_string_to_word(name.as_ref());
57        Self(digest_word[0].as_canonical_u64())
58    }
59
60    /// Creates an EventId from a [`Felt`] value (e.g., from the stack).
61    pub fn from_felt(event_id: Felt) -> Self {
62        Self(event_id.as_canonical_u64())
63    }
64
65    /// Creates an EventId from a `u64` value, reducing modulo the field prime.
66    pub const fn from_u64(event_id: u64) -> Self {
67        Self(event_id % Felt::ORDER_U64)
68    }
69
70    /// Converts this event ID to a [`Felt`].
71    pub const fn as_felt(&self) -> Felt {
72        Felt::new(self.0)
73    }
74
75    /// Returns the inner `u64` representation.
76    pub const fn as_u64(&self) -> u64 {
77        self.0
78    }
79}
80
81impl Display for EventId {
82    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
83        core::fmt::Display::fmt(&self.0, f)
84    }
85}
86
87// EVENT NAME
88// ================================================================================================
89
90/// A human-readable name for an event.
91///
92/// [`EventName`] is used for:
93/// - Event handler registration (EventId computed from name at registration time)
94/// - Error messages and debugging
95/// - Resolving EventIds back to names via the event registry
96///
97/// System events use the "sys::" namespace prefix to distinguish them from user-defined events.
98///
99/// For event identification during execution (e.g., reading from the stack), use [`EventId`]
100/// directly. Names can be looked up via the event registry when needed for error reporting.
101#[derive(Debug, Clone, PartialEq, Eq)]
102#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
103#[cfg_attr(feature = "serde", serde(transparent))]
104#[cfg_attr(
105    all(feature = "arbitrary", test),
106    miden_test_serde_macros::serde_test(binary_serde(true))
107)]
108pub struct EventName(Cow<'static, str>);
109
110impl EventName {
111    /// Creates an EventName from a static string.
112    ///
113    /// This is the primary constructor for compile-time event name constants.
114    pub const fn new(name: &'static str) -> Self {
115        Self(Cow::Borrowed(name))
116    }
117
118    /// Creates an EventName from an owned String.
119    ///
120    /// Use this for dynamically constructed event names (e.g., in error messages).
121    pub fn from_string(name: String) -> Self {
122        Self(Cow::Owned(name))
123    }
124
125    /// Returns the event name as a string slice.
126    pub fn as_str(&self) -> &str {
127        self.0.as_ref()
128    }
129
130    /// Returns the [`EventId`] for this event name.
131    ///
132    /// The ID is computed by hashing the name using blake3.
133    pub fn to_event_id(&self) -> EventId {
134        EventId::from_name(self.as_str())
135    }
136}
137
138impl Display for EventName {
139    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
140        write!(f, "{}", self.0)
141    }
142}
143
144impl AsRef<str> for EventName {
145    fn as_ref(&self) -> &str {
146        self.0.as_ref()
147    }
148}
149
150// SERIALIZATION
151// ================================================================================================
152
153impl Serializable for EventId {
154    fn write_into<W: ByteWriter>(&self, target: &mut W) {
155        self.as_felt().write_into(target);
156    }
157}
158
159impl Deserializable for EventId {
160    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
161        Ok(Self(Felt::read_from(source)?.as_canonical_u64()))
162    }
163}
164
165impl Serializable for EventName {
166    fn write_into<W: ByteWriter>(&self, target: &mut W) {
167        // Serialize as a string (supports both Borrowed and Owned variants)
168        self.0.as_ref().write_into(target)
169    }
170}
171
172impl Deserializable for EventName {
173    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
174        let name = String::read_from(source)?;
175        Ok(Self::from_string(name))
176    }
177}
178
179// TESTING
180// ================================================================================================
181
182#[cfg(all(feature = "arbitrary", test))]
183impl proptest::prelude::Arbitrary for EventId {
184    type Parameters = ();
185
186    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
187        use proptest::prelude::*;
188        any::<u64>().prop_map(EventId::from_u64).boxed()
189    }
190
191    type Strategy = proptest::prelude::BoxedStrategy<Self>;
192}
193
194#[cfg(all(feature = "arbitrary", test))]
195impl proptest::prelude::Arbitrary for EventName {
196    type Parameters = ();
197
198    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
199        use proptest::prelude::*;
200
201        // Test both Cow::Borrowed (static) and Cow::Owned (dynamic) variants
202        prop_oneof![
203            // Static strings (Cow::Borrowed)
204            Just(EventName::new("test::static::event")),
205            Just(EventName::new("core::handler::example")),
206            Just(EventName::new("user::custom::event")),
207            // Dynamic strings (Cow::Owned)
208            any::<(u32, u32)>()
209                .prop_map(|(a, b)| EventName::from_string(format!("dynamic::event::{}::{}", a, b))),
210        ]
211        .boxed()
212    }
213
214    type Strategy = proptest::prelude::BoxedStrategy<Self>;
215}
216
217// TESTS
218// ================================================================================================
219
220#[cfg(test)]
221mod tests {
222    use alloc::string::ToString;
223
224    use super::*;
225
226    #[test]
227    fn event_basics() {
228        // EventId constructors and conversions
229        let id1 = EventId::from_u64(100);
230        assert_eq!(id1.as_u64(), 100);
231        assert_eq!(id1.as_felt(), Felt::new(100));
232
233        let id2 = EventId::from_felt(Felt::new(200));
234        assert_eq!(id2.as_u64(), 200);
235
236        // EventId from name hashes consistently
237        let id3 = EventId::from_name("test::event");
238        let id4 = EventId::from_name("test::event");
239        assert_eq!(id3, id4);
240
241        // EventName constructors and conversions
242        let name1 = EventName::new("static::event");
243        assert_eq!(name1.as_str(), "static::event");
244        assert_eq!(format!("{}", name1), "static::event");
245
246        let name2 = EventName::from_string("dynamic::event".to_string());
247        assert_eq!(name2.as_str(), "dynamic::event");
248
249        // EventName to EventId
250        assert_eq!(name1.to_event_id(), EventId::from_name("static::event"));
251    }
252}