miden_core/
event_id.rs

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