tendermint/abci/
event.rs

1use serde::{Deserialize, Serialize};
2
3use crate::prelude::*;
4
5/// An event that occurred while processing a request.
6///
7/// Application developers can attach additional information to
8/// [`BeginBlock`](super::response::BeginBlock),
9/// [`EndBlock`](super::response::EndBlock),
10/// [`CheckTx`](super::response::CheckTx), and
11/// [`DeliverTx`](super::response::DeliverTx) responses. Later, transactions may
12/// be queried using these events.
13///
14/// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events)
15#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Hash)]
16pub struct Event {
17    /// The kind of event.
18    ///
19    /// Tendermint calls this the `type`, but we use `kind` to avoid confusion
20    /// with Rust types and follow Rust conventions.
21    #[serde(rename = "type", default)]
22    pub kind: String,
23
24    /// A list of [`EventAttribute`]s describing the event.
25    pub attributes: Vec<EventAttribute>,
26}
27
28/// The attributes of an Event consist of a key, a value, and an index flag.
29/// The index flag notifies the Tendermint indexer to index the attribute.
30/// The value of the index flag is non-deterministic and may vary across different nodes in the network.
31///
32/// Before Tendermint v0.37, the key and value can contain arbitrary byte arrays.
33/// Since Tendermint v0.37, the key and value are defined to be valid UTF-8 encoded strings.
34///
35/// IMPORTANT: The order of the two variants below is significant and must not be changed.
36/// The `EventAttribute` enum is serialized and deserialized using the `untagged` attribute,
37/// meaning that the first variant is tried first when deserializing, if that fails
38/// then the second variant is tried. This allows us to deserialize v0.37+ events which
39/// are vald UTF-8 strings, and fall back to deserializing v0.34 events which are arbitrary byte arrays.
40#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Hash)]
41#[serde(untagged)]
42pub enum EventAttribute {
43    /// EventAttribute keys and values in TM37 and later are plain strings.
44    V037(v0_37::EventAttribute),
45
46    /// EventAttribute keys and values in TM34 are base64-encoded strings.
47    V034(v0_34::EventAttribute),
48}
49
50impl EventAttribute {
51    /// Access the `key` field common to all variants of the enum as a string.
52    /// Will return error if the value is malformed UTF8.
53    pub fn key_str(&self) -> Result<&str, crate::Error> {
54        match self {
55            EventAttribute::V034(attr) => {
56                core::str::from_utf8(&attr.key).map_err(|e| crate::Error::parse(e.to_string()))
57            },
58            EventAttribute::V037(attr) => Ok(&attr.key),
59        }
60    }
61
62    /// Access the `key` field common to all variants of the enum as bytes.
63    pub fn key_bytes(&self) -> &[u8] {
64        match self {
65            EventAttribute::V034(attr) => &attr.key,
66            EventAttribute::V037(attr) => attr.key.as_bytes(),
67        }
68    }
69
70    /// Access the `value` field common to all variants of the enum as a string.
71    /// Will return error if the value is malformed UTF8.
72    pub fn value_str(&self) -> Result<&str, crate::Error> {
73        match self {
74            EventAttribute::V034(attr) => {
75                core::str::from_utf8(&attr.value).map_err(|e| crate::Error::parse(e.to_string()))
76            },
77            EventAttribute::V037(attr) => Ok(&attr.value),
78        }
79    }
80
81    /// Access the `value` field common to all variants of the enum as bytes.
82    /// This is useful if you have binary values for TM34.
83    pub fn value_bytes(&self) -> &[u8] {
84        match self {
85            EventAttribute::V034(attr) => &attr.value,
86            EventAttribute::V037(attr) => attr.value.as_bytes(),
87        }
88    }
89
90    /// Access the `index` field common to all variants of the enum.
91    pub fn index(&self) -> bool {
92        match self {
93            EventAttribute::V034(attr) => attr.index,
94            EventAttribute::V037(attr) => attr.index,
95        }
96    }
97
98    /// Set `index` field common to all variants of the enum.
99    pub fn set_index(&mut self, index: bool) {
100        match self {
101            EventAttribute::V034(attr) => attr.index = index,
102            EventAttribute::V037(attr) => attr.index = index,
103        }
104    }
105}
106
107impl Event {
108    /// Construct an event from generic data.
109    ///
110    /// The `From` impls on [`EventAttribute`] and the [`EventAttributeIndexExt`]
111    /// trait allow ergonomic event construction, as in this example:
112    ///
113    /// ```
114    /// use tendermint::abci::{Event, EventAttributeIndexExt};
115    ///
116    /// let event = Event::new(
117    ///     "app",
118    ///     [
119    ///         ("key1", "value1").index(),
120    ///         ("key2", "value2").index(),
121    ///         ("key3", "value3").no_index(), // will not be indexed
122    ///     ],
123    /// );
124    /// ```
125    pub fn new<K, I>(kind: K, attributes: I) -> Self
126    where
127        K: Into<String>,
128        I: IntoIterator,
129        I::Item: Into<EventAttribute>,
130    {
131        Self {
132            kind: kind.into(),
133            attributes: attributes.into_iter().map(Into::into).collect(),
134        }
135    }
136
137    /// Checks whether `&self` is equal to `other`, ignoring the `index` field on any attributes.
138    pub fn eq_ignoring_index(&self, other: &Self) -> bool {
139        self.kind == other.kind
140            // IMPORTANT! We need to check the lengths before calling zip,
141            // in order to not drop any attributes.
142            && self.attributes.len() == other.attributes.len()
143            && self
144                .attributes
145                .iter()
146                .zip(other.attributes.iter())
147                .all(|(a, b)| a.eq_ignoring_index(b))
148    }
149
150    /// A variant of [`core::hash::Hash::hash`] that ignores the `index` field on any attributes.
151    pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
152        use core::hash::Hash;
153        self.kind.hash(state);
154        // We can't forward to the slice impl here, because we need to call `hash_ignoring_index`
155        // on each attribute, so we need to do our own length prefixing.
156        state.write_usize(self.attributes.len());
157        for attr in &self.attributes {
158            attr.hash_ignoring_index(state);
159        }
160    }
161}
162
163/// A marker trait for types that can be converted to and from [`Event`]s.
164///
165/// This trait doesn't make any assumptions about how the conversion is
166/// performed, or how the type's data is encoded in event attributes.  Instead,
167/// it just declares the conversion methods used to serialize the type to an
168/// [`Event`] and to deserialize it from an [`Event`], allowing downstream users
169/// to declare a single source of truth about how event data is structured.
170///
171/// # Contract
172///
173/// If `T: TypedEvent`, then:
174///
175/// - `T::try_from(e) == Ok(t)` for all `t: T, e: Event` where `Event::from(t).eq_ignoring_index(e)
176///   == true`.
177/// - `Event::from(T::try_from(e).unwrap()).eq_ignoring_index(e) == true` for all `e: Event` where
178///   `T::try_from(e)` returns `Ok(_)`.
179///
180/// In other words, the conversion methods should round-trip on the attributes,
181/// but are not required to preserve the (nondeterministic) index information.
182pub trait TypedEvent
183where
184    Self: TryFrom<Event>,
185    Event: From<Self>,
186{
187    /// Convenience wrapper around `Into::into` that doesn't require type inference.
188    fn into_event(self) -> Event {
189        self.into()
190    }
191}
192
193impl EventAttribute {
194    /// Checks whether `&self` is equal to `other`, ignoring the `index` field.
195    pub fn eq_ignoring_index(&self, other: &Self) -> bool {
196        match (self, other) {
197            (EventAttribute::V034(a), EventAttribute::V034(b)) => {
198                a.key == b.key && a.value == b.value
199            },
200            (EventAttribute::V037(a), EventAttribute::V037(b)) => {
201                a.key == b.key && a.value == b.value
202            },
203            // Shouldn't happen, comparing event attributes from different versions
204            _ => false,
205        }
206    }
207
208    /// A variant of [`core::hash::Hash::hash`] that ignores the `index` field.
209    pub fn hash_ignoring_index<H: core::hash::Hasher>(&self, state: &mut H) {
210        use core::hash::Hash;
211        // Call the `Hash` impl on the (k,v) tuple to avoid prefix collision issues.
212        match self {
213            EventAttribute::V034(attr) => {
214                (&attr.key, &attr.value).hash(state);
215            },
216            EventAttribute::V037(attr) => {
217                (&attr.key, &attr.value).hash(state);
218            },
219        }
220    }
221}
222
223impl<K: Into<String>, V: Into<String>> From<(K, V, bool)> for EventAttribute {
224    fn from((key, value, index): (K, V, bool)) -> Self {
225        Self::V037(v0_37::EventAttribute {
226            key: key.into(),
227            value: value.into(),
228            index,
229        })
230    }
231}
232
233/// Adds convenience methods to tuples for more ergonomic [`EventAttribute`]
234/// construction.
235///
236/// See [`Event::new`] for details.
237#[allow(missing_docs)]
238pub trait EventAttributeIndexExt: private::Sealed {
239    type Key;
240    type Value;
241
242    /// Indicate that this key/value pair should be indexed by Tendermint.
243    fn index(self) -> (Self::Key, Self::Value, bool);
244    /// Indicate that this key/value pair should not be indexed by Tendermint.
245    fn no_index(self) -> (Self::Key, Self::Value, bool);
246}
247
248impl<K: Into<String>, V: Into<String>> EventAttributeIndexExt for (K, V) {
249    type Key = K;
250    type Value = V;
251    fn index(self) -> (K, V, bool) {
252        let (key, value) = self;
253        (key, value, true)
254    }
255    fn no_index(self) -> (K, V, bool) {
256        let (key, value) = self;
257        (key, value, false)
258    }
259}
260
261mod private {
262    use crate::prelude::*;
263
264    pub trait Sealed {}
265
266    impl<K: Into<String>, V: Into<String>> Sealed for (K, V) {}
267}
268
269impl<K: Into<String>, V: Into<String>> From<(K, V)> for EventAttribute {
270    fn from((key, value): (K, V)) -> Self {
271        (key, value, false).into()
272    }
273}
274
275// =============================================================================
276// Protobuf conversions
277// =============================================================================
278
279pub mod v0_34 {
280    use super::Event;
281    use crate::prelude::*;
282    use crate::serializers;
283
284    use serde::{Deserialize, Serialize};
285    use tendermint_proto::v0_34::abci as pb;
286    use tendermint_proto::Protobuf;
287
288    /// A key-value pair describing an [`Event`].
289    ///
290    /// Generic methods are provided for more ergonomic attribute construction, see
291    /// [`Event::new`] for details.
292    ///
293    /// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events)
294    #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
295    pub struct EventAttribute {
296        /// The event key.
297        #[serde(with = "serializers::bytes::base64string")]
298        pub key: Vec<u8>,
299
300        /// The event value.
301        #[serde(with = "serializers::bytes::base64string")]
302        pub value: Vec<u8>,
303
304        /// Whether Tendermint's indexer should index this event.
305        ///
306        /// **This field is nondeterministic**.
307        pub index: bool,
308    }
309
310    impl From<EventAttribute> for pb::EventAttribute {
311        fn from(event: EventAttribute) -> Self {
312            Self {
313                key: event.key.into(),
314                value: event.value.into(),
315                index: event.index,
316            }
317        }
318    }
319
320    impl TryFrom<pb::EventAttribute> for EventAttribute {
321        type Error = crate::Error;
322
323        fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
324            Ok(Self {
325                key: event.value.to_vec(),
326                value: event.value.to_vec(),
327                index: event.index,
328            })
329        }
330    }
331
332    impl Protobuf<pb::EventAttribute> for EventAttribute {}
333
334    impl From<Event> for pb::Event {
335        fn from(event: Event) -> Self {
336            Self {
337                r#type: event.kind,
338                attributes: event
339                    .attributes
340                    .into_iter()
341                    .filter_map(|t| {
342                        let super::EventAttribute::V034(ea) = t else {
343                            return None;
344                        };
345                        Some(ea.into())
346                    })
347                    .collect(),
348            }
349        }
350    }
351
352    impl TryFrom<pb::Event> for Event {
353        type Error = crate::Error;
354
355        fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
356            Ok(Self {
357                kind: event.r#type,
358                attributes: event
359                    .attributes
360                    .into_iter()
361                    .map(TryInto::try_into)
362                    .collect::<Result<Vec<EventAttribute>, _>>()?
363                    .into_iter()
364                    .map(super::EventAttribute::V034)
365                    .collect(),
366            })
367        }
368    }
369
370    impl Protobuf<pb::Event> for Event {}
371}
372
373pub mod v0_37 {
374    use super::Event;
375    use crate::prelude::*;
376    use crate::serializers;
377
378    use serde::{Deserialize, Serialize};
379    use tendermint_proto::v0_37::abci as pb;
380    use tendermint_proto::Protobuf;
381
382    /// A key-value pair describing an [`Event`].
383    ///
384    /// Generic methods are provided for more ergonomic attribute construction, see
385    /// [`Event::new`] for details.
386    ///
387    /// [ABCI documentation](https://docs.tendermint.com/master/spec/abci/abci.html#events)
388    #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
389    pub struct EventAttribute {
390        /// The event key.
391        #[serde(with = "serializers::allow_null")]
392        pub key: String,
393
394        /// The event value.
395        #[serde(with = "serializers::allow_null")]
396        pub value: String,
397
398        /// Whether Tendermint's indexer should index this event.
399        ///
400        /// **This field is nondeterministic**.
401        pub index: bool,
402    }
403
404    impl From<EventAttribute> for pb::EventAttribute {
405        fn from(event: EventAttribute) -> Self {
406            Self {
407                key: event.key,
408                value: event.value,
409                index: event.index,
410            }
411        }
412    }
413
414    impl TryFrom<pb::EventAttribute> for EventAttribute {
415        type Error = crate::Error;
416
417        fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
418            Ok(Self {
419                key: event.key,
420                value: event.value,
421                index: event.index,
422            })
423        }
424    }
425
426    impl Protobuf<pb::EventAttribute> for EventAttribute {}
427
428    impl From<Event> for pb::Event {
429        fn from(event: Event) -> Self {
430            Self {
431                r#type: event.kind,
432                attributes: event
433                    .attributes
434                    .into_iter()
435                    .filter_map(|t| {
436                        let super::EventAttribute::V037(ea) = t else {
437                            return None;
438                        };
439                        Some(ea.into())
440                    })
441                    .collect(),
442            }
443        }
444    }
445
446    impl TryFrom<pb::Event> for Event {
447        type Error = crate::Error;
448
449        fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
450            Ok(Self {
451                kind: event.r#type,
452                attributes: event
453                    .attributes
454                    .into_iter()
455                    .map(TryInto::try_into)
456                    .collect::<Result<Vec<EventAttribute>, _>>()?
457                    .into_iter()
458                    .map(super::EventAttribute::V037)
459                    .collect(),
460            })
461        }
462    }
463
464    impl Protobuf<pb::Event> for Event {}
465}
466
467mod v0_38 {
468    use super::Event;
469    use crate::prelude::*;
470
471    use tendermint_proto::v0_38::abci as pb;
472    use tendermint_proto::Protobuf;
473
474    pub use super::v0_37::EventAttribute;
475
476    impl From<EventAttribute> for pb::EventAttribute {
477        fn from(event: EventAttribute) -> Self {
478            Self {
479                key: event.key,
480                value: event.value,
481                index: event.index,
482            }
483        }
484    }
485
486    impl TryFrom<pb::EventAttribute> for EventAttribute {
487        type Error = crate::Error;
488
489        fn try_from(event: pb::EventAttribute) -> Result<Self, Self::Error> {
490            Ok(Self {
491                key: event.key,
492                value: event.value,
493                index: event.index,
494            })
495        }
496    }
497
498    impl Protobuf<pb::EventAttribute> for EventAttribute {}
499
500    impl From<Event> for pb::Event {
501        fn from(event: Event) -> Self {
502            Self {
503                r#type: event.kind,
504                attributes: event
505                    .attributes
506                    .into_iter()
507                    .filter_map(|t| {
508                        let super::EventAttribute::V037(ea) = t else {
509                            return None;
510                        };
511                        Some(ea.into())
512                    })
513                    .collect(),
514            }
515        }
516    }
517
518    impl TryFrom<pb::Event> for Event {
519        type Error = crate::Error;
520
521        fn try_from(event: pb::Event) -> Result<Self, Self::Error> {
522            Ok(Self {
523                kind: event.r#type,
524                attributes: event
525                    .attributes
526                    .into_iter()
527                    .map(TryInto::try_into)
528                    .collect::<Result<Vec<EventAttribute>, _>>()?
529                    .into_iter()
530                    .map(super::EventAttribute::V037)
531                    .collect(),
532            })
533        }
534    }
535
536    impl Protobuf<pb::Event> for Event {}
537}
538
539#[cfg(test)]
540#[allow(clippy::bool_assert_comparison)]
541#[allow(clippy::redundant_clone)]
542mod tests {
543
544    use serde::Deserialize;
545
546    use super::*;
547
548    #[test]
549    fn event_eq_ignoring_index_ignores_index() {
550        let event_a = Event::new("test", [("foo", "bar").index()]);
551        let event_b = Event::new("test", [("foo", "bar").no_index()]);
552        let event_c = Event::new("test", [("foo", "baz").index()]);
553
554        assert_eq!(event_a.eq_ignoring_index(&event_b), true);
555        assert_eq!(event_a.eq_ignoring_index(&event_c), false);
556    }
557
558    #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
559    struct Payload {
560        x: u32,
561        y: u32,
562    }
563
564    #[derive(Clone, Debug, PartialEq, Eq)]
565    struct MyEvent {
566        a: Payload,
567        b: Payload,
568    }
569
570    impl From<MyEvent> for Event {
571        fn from(event: MyEvent) -> Self {
572            Event::new(
573                "my_event",
574                vec![
575                    ("a", serde_json::to_string(&event.a).unwrap()).index(),
576                    ("b", serde_json::to_string(&event.b).unwrap()).index(),
577                ],
578            )
579        }
580    }
581
582    impl TryFrom<Event> for MyEvent {
583        type Error = (); // Avoid depending on a specific error library in test code
584
585        fn try_from(event: Event) -> Result<Self, Self::Error> {
586            if event.kind != "my_event" {
587                return Err(());
588            }
589
590            let a = event
591                .attributes
592                .iter()
593                .find(|attr| attr.key_bytes() == b"a")
594                .ok_or(())
595                .and_then(|attr| serde_json::from_slice(attr.value_bytes()).map_err(|_| ()))?;
596            let b = event
597                .attributes
598                .iter()
599                .find(|attr| attr.key_bytes() == b"b")
600                .ok_or(())
601                .and_then(|attr| serde_json::from_slice(attr.value_bytes()).map_err(|_| ()))?;
602
603            Ok(MyEvent { a, b })
604        }
605    }
606
607    impl TypedEvent for MyEvent {}
608
609    #[test]
610    fn exercise_typed_event() {
611        let t = MyEvent {
612            a: Payload { x: 1, y: 2 },
613            b: Payload { x: 3, y: 4 },
614        };
615
616        let e1 = Event::from(t.clone());
617        // e2 is like e1 but with different indexing.
618        let e2 = {
619            let mut e = e1.clone();
620            e.attributes[0].set_index(false);
621            e.attributes[1].set_index(false);
622            e
623        };
624
625        // Contract:
626
627        // - `T::try_from(e) == Ok(t)` for all `t: T, e: Event` where
628        //   `Event::from(t).eq_ignoring_index(e) == true`.
629        assert_eq!(e1.eq_ignoring_index(&e2), true);
630        assert_eq!(MyEvent::try_from(e1.clone()), Ok(t.clone()));
631        assert_eq!(MyEvent::try_from(e2.clone()), Ok(t.clone()));
632
633        // - `Event::from(T::try_from(e).unwrap()).eq_ignoring_index(e) == true` for all `e: Event`
634        //   where `T::try_from(e)` returns `Ok(_)`.
635        assert_eq!(
636            Event::from(MyEvent::try_from(e1.clone()).unwrap()).eq_ignoring_index(&e1),
637            true
638        );
639        assert_eq!(
640            Event::from(MyEvent::try_from(e2.clone()).unwrap()).eq_ignoring_index(&e2),
641            true
642        );
643    }
644}