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